From fijal at codespeak.net Thu Feb 1 12:28:48 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Thu, 1 Feb 2007 12:28:48 +0100 (CET) Subject: [py-svn] r37720 - py/trunk/py/test/rsession Message-ID: <20070201112848.C085E10088@code0.codespeak.net> Author: fijal Date: Thu Feb 1 12:28:45 2007 New Revision: 37720 Modified: py/trunk/py/test/rsession/webjs.py Log: Kill dead import Modified: py/trunk/py/test/rsession/webjs.py ============================================================================== --- py/trunk/py/test/rsession/webjs.py (original) +++ py/trunk/py/test/rsession/webjs.py Thu Feb 1 12:28:45 2007 @@ -7,7 +7,6 @@ try: 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: py.test.skip("PyPy not found") From fijal at codespeak.net Thu Feb 1 12:50:38 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Thu, 1 Feb 2007 12:50:38 +0100 (CET) Subject: [py-svn] r37721 - py/trunk/py/test/rsession Message-ID: <20070201115038.BF36810071@code0.codespeak.net> Author: fijal Date: Thu Feb 1 12:50:29 2007 New Revision: 37721 Modified: py/trunk/py/test/rsession/executor.py Log: Fix. !Tests needed! Modified: py/trunk/py/test/rsession/executor.py ============================================================================== --- py/trunk/py/test/rsession/executor.py (original) +++ py/trunk/py/test/rsession/executor.py Thu Feb 1 12:50:29 2007 @@ -60,21 +60,19 @@ self.tracer = tracer return super(ApigenExecutor, self).execute() - def wrap_underlaying(self, target): - def f(*args): - try: - self.tracer.start_tracing() - return target(*args) - finally: - self.tracer.end_tracing() - return f + def wrap_underlaying(self, target, *args): + try: + self.tracer.start_tracing() + return target(*args) + finally: + self.tracer.end_tracing() def run(self): """ We want to trace *only* function objects here. Unsure what to do with custom collectors at all """ - if hasattr(self.item, 'obj') and type(self.item.obj) is py.test.Function: - self.item.obj = self.wrap_underlaying(self.item.obj) + if hasattr(self.item, 'obj') and type(self.item) is py.test.Function: + self.item.execute = self.wrap_underlaying self.item.run() class BoxExecutor(RunExecutor): From fijal at codespeak.net Thu Feb 1 12:59:48 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Thu, 1 Feb 2007 12:59:48 +0100 (CET) Subject: [py-svn] r37723 - py/trunk/py/test/rsession/testing Message-ID: <20070201115948.BB13F10077@code0.codespeak.net> Author: fijal Date: Thu Feb 1 12:59:47 2007 New Revision: 37723 Modified: py/trunk/py/test/rsession/testing/test_executor.py Log: Add a test Modified: py/trunk/py/test/rsession/testing/test_executor.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_executor.py (original) +++ py/trunk/py/test/rsession/testing/test_executor.py Thu Feb 1 12:59:47 2007 @@ -3,7 +3,7 @@ import example1 from py.__.test.rsession.executor import RunExecutor, BoxExecutor,\ - AsyncExecutor + AsyncExecutor, ApigenExecutor from py.__.test.rsession.outcome import ReprOutcome from py.__.test.rsession.testing.test_slave import funcprint_spec, \ funcprintfail_spec @@ -120,3 +120,51 @@ outcome = ReprOutcome(outcome_repr) assert not outcome.passed assert outcome.stdout.find("samfing elz") != -1 + +def test_apigen_executor(): + class Tracer(object): + def __init__(self): + self.starts = 0 + self.ends = 0 + + def start_tracing(self): + self.starts += 1 + + def end_tracing(self): + self.ends += 1 + + tmpdir = py.test.ensuretemp("apigen_executor") + tmpdir.ensure("__init__.py") + tmpdir.ensure("test_one.py").write(py.code.Source(""" + def g(): + pass + + def test_1(): + g() + + class TestX(object): + def setup_method(self, m): + self.ttt = 1 + + def test_one(self): + self.ttt += 1 + + def test_raise(self): + 1/0 + """)) + rootcol = py.test.collect.Directory(tmpdir) + tracer = Tracer() + item = rootcol.getitembynames("test_one.py/test_1") + ex = ApigenExecutor(item, config=config) + out1 = ex.execute(tracer) + item = rootcol.getitembynames("test_one.py/TestX/()/test_one") + ex = ApigenExecutor(item, config=config) + out2 = ex.execute(tracer) + item = rootcol.getitembynames("test_one.py/TestX/()/test_raise") + ex = ApigenExecutor(item, config=config) + out3 = ex.execute(tracer) + assert tracer.starts == 3 + assert tracer.ends == 3 + assert out1.passed + assert out2.passed + assert not out3.passed From guido at codespeak.net Thu Feb 1 14:56:33 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Thu, 1 Feb 2007 14:56:33 +0100 (CET) Subject: [py-svn] r37729 - in py/trunk/py: apigen io io/test test/rsession Message-ID: <20070201135633.7A2E210070@code0.codespeak.net> Author: guido Date: Thu Feb 1 14:56:31 2007 New Revision: 37729 Modified: py/trunk/py/apigen/apigen.py py/trunk/py/io/capture.py py/trunk/py/io/test/test_capture.py py/trunk/py/test/rsession/rsession.py Log: Added some code to py.io.FDCapture and py.io.OutErrCapture to allow writing to the original (patched) file descriptor. Also made that the capturing object is passed to apigen.py's build() function (from rsession.py), which uses the new methods to print progress information. Modified: py/trunk/py/apigen/apigen.py ============================================================================== --- py/trunk/py/apigen/apigen.py (original) +++ py/trunk/py/apigen/apigen.py Thu Feb 1 14:56:31 2007 @@ -17,7 +17,7 @@ rootmod = __import__(pkgdir.basename) return 'py', pkg_to_dict(rootmod) -def build(pkgdir, dsa): +def build(pkgdir, dsa, capture): l = linker.Linker() proj = project.Project() @@ -32,16 +32,25 @@ apb = htmlgen.ApiPageBuilder(targetdir, l, dsa, pkgdir, namespace_tree) spb = htmlgen.SourcePageBuilder(targetdir, l, pkgdir) + capture.writeorgerr('preparing namespace pages\n') ns_data = apb.prepare_namespace_pages() + capture.writeorgerr('preparing class pages\n') class_names = dsa.get_class_names() class_data = apb.prepare_class_pages(class_names) + capture.writeorgerr('preparing function pages\n') function_names = dsa.get_function_names() func_data = apb.prepare_function_pages(function_names) + capture.writeorgerr('preparing source pages\n') source_data = spb.prepare_pages(pkgdir) + capture.writeorgerr('building namespace pages\n') apb.build_namespace_pages(ns_data, proj) + capture.writeorgerr('building class pages\n') apb.build_class_pages(class_data, proj) - #apb.build_method_pages(method_data, proj) + apb.build_method_pages(method_data, proj) + capture.writeorgerr('building function pages\n') apb.build_function_pages(func_data, proj) + capture.writeorgerr('building source pages\n') spb.build_pages(source_data, proj, pkgdir) + capture.writeorgerr('done building documentation\n') Modified: py/trunk/py/io/capture.py ============================================================================== --- py/trunk/py/io/capture.py (original) +++ py/trunk/py/io/capture.py Thu Feb 1 14:56:31 2007 @@ -1,9 +1,12 @@ -import os, sys +import os +import sys +import thread import py class FDCapture: """ Capture IO to/from a given os-level filedescriptor. """ + def __init__(self, targetfd, tmpfile=None): self.targetfd = targetfd if tmpfile is None: @@ -14,16 +17,22 @@ self._patched = [] def setasfile(self, name, module=sys): + """ patch . to self.tmpfile + """ key = (module, name) self._patched.append((key, getattr(module, name))) setattr(module, name, self.tmpfile) def unsetfiles(self): + """ unpatch all patched items + """ while self._patched: (module, name), value = self._patched.pop() setattr(module, name, value) def done(self): + """ unpatch and clean up, returns the self.tmpfile (file object) + """ os.dup2(self._savefd, self.targetfd) self.unsetfiles() os.close(self._savefd) @@ -31,11 +40,23 @@ return self.tmpfile def maketmpfile(self): + """ create a temporary file + """ f = os.tmpfile() newf = py.io.dupfile(f) f.close() return newf + def writeorg(self, str): + """ write a string to the original file descriptor + """ + tempfp = os.tmpfile() + try: + os.dup2(self._savefd, tempfp.fileno()) + tempfp.write(str) + finally: + tempfp.close() + class OutErrCapture: """ capture Stdout and Stderr both on filedescriptor and sys.stdout/stderr level. @@ -51,6 +72,11 @@ self.err.setasfile('stderr') def reset(self): + """ reset sys.stdout and sys.stderr + + returns a tuple of file objects (out, err) for the captured + data + """ out = err = "" if hasattr(self, 'out'): outfile = self.out.done() @@ -60,6 +86,20 @@ err = errfile.read() return out, err + def writeorgout(self, str): + """ write something to the original stdout + """ + if not hasattr(self, 'out'): + raise IOError('stdout not patched') + self.out.writeorg(str) + + def writeorgerr(self, str): + """ write something to the original stderr + """ + if not hasattr(self, 'err'): + raise IOError('stderr not patched') + self.err.writeorg(str) + def callcapture(func, *args, **kwargs): """ call the given function with args/kwargs and return a (res, out, err) tuple where Modified: py/trunk/py/io/test/test_capture.py ============================================================================== --- py/trunk/py/io/test/test_capture.py (original) +++ py/trunk/py/io/test/test_capture.py Thu Feb 1 14:56:31 2007 @@ -30,6 +30,33 @@ f = cap.done() assert x == "3" + def test_writeorg(self): + tmppath = py.test.ensuretemp('test_writeorg').ensure('stderr', + file=True) + tmpfp = tmppath.open('w+b') + try: + cap = py.io.FDCapture(tmpfp.fileno()) + print >>tmpfp, 'foo' + cap.writeorg('bar\n') + finally: + tmpfp.close() + f = cap.done() + scap = f.read() + assert scap == 'foo\n' + stmp = tmppath.read() + assert stmp == "bar\n" + + def test_writeorg_wrongtype(self): + tmppath = py.test.ensuretemp('test_writeorg').ensure('stdout', + file=True) + tmpfp = tmppath.open('r') + try: + cap = py.io.FDCapture(tmpfp.fileno()) + py.test.raises(IOError, "cap.writeorg('bar\\n')") + finally: + tmpfp.close() + f = cap.done() + class TestCapturing: def getcapture(self): return py.io.OutErrCapture() Modified: py/trunk/py/test/rsession/rsession.py ============================================================================== --- py/trunk/py/test/rsession/rsession.py (original) +++ py/trunk/py/test/rsession/rsession.py Thu Feb 1 14:56:31 2007 @@ -263,7 +263,8 @@ try: pkgdir = self.getpkgdir(self.config.args[0]) apigen.build(pkgdir, - DocStorageAccessor(self.docstorage)) + DocStorageAccessor(self.docstorage), + capture) finally: capture.reset() From guido at codespeak.net Thu Feb 1 15:14:06 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Thu, 1 Feb 2007 15:14:06 +0100 (CET) Subject: [py-svn] r37733 - in py/trunk/py: apigen io Message-ID: <20070201141406.1F26D10084@code0.codespeak.net> Author: guido Date: Thu Feb 1 15:13:59 2007 New Revision: 37733 Modified: py/trunk/py/apigen/apigen.py py/trunk/py/io/capture.py Log: Removed two rather useless methods that just delegated to methods on the underlying object, in favour of calling the underlying object's methods directly (py.io.OutErrCapture.writeorg*). Modified: py/trunk/py/apigen/apigen.py ============================================================================== --- py/trunk/py/apigen/apigen.py (original) +++ py/trunk/py/apigen/apigen.py Thu Feb 1 15:13:59 2007 @@ -32,25 +32,25 @@ apb = htmlgen.ApiPageBuilder(targetdir, l, dsa, pkgdir, namespace_tree) spb = htmlgen.SourcePageBuilder(targetdir, l, pkgdir) - capture.writeorgerr('preparing namespace pages\n') + capture.err.writeorg('preparing namespace pages\n') ns_data = apb.prepare_namespace_pages() - capture.writeorgerr('preparing class pages\n') + capture.err.writeorg('preparing class pages\n') class_names = dsa.get_class_names() class_data = apb.prepare_class_pages(class_names) - capture.writeorgerr('preparing function pages\n') + capture.err.writeorg('preparing function pages\n') function_names = dsa.get_function_names() func_data = apb.prepare_function_pages(function_names) - capture.writeorgerr('preparing source pages\n') + capture.err.writeorg('preparing source pages\n') source_data = spb.prepare_pages(pkgdir) - capture.writeorgerr('building namespace pages\n') + capture.err.writeorg('building namespace pages\n') apb.build_namespace_pages(ns_data, proj) - capture.writeorgerr('building class pages\n') + capture.err.writeorg('building class pages\n') apb.build_class_pages(class_data, proj) apb.build_method_pages(method_data, proj) - capture.writeorgerr('building function pages\n') + capture.err.writeorg('building function pages\n') apb.build_function_pages(func_data, proj) - capture.writeorgerr('building source pages\n') + capture.err.writeorg('building source pages\n') spb.build_pages(source_data, proj, pkgdir) - capture.writeorgerr('done building documentation\n') + capture.err.writeorg('done building documentation\n') Modified: py/trunk/py/io/capture.py ============================================================================== --- py/trunk/py/io/capture.py (original) +++ py/trunk/py/io/capture.py Thu Feb 1 15:13:59 2007 @@ -86,20 +86,6 @@ err = errfile.read() return out, err - def writeorgout(self, str): - """ write something to the original stdout - """ - if not hasattr(self, 'out'): - raise IOError('stdout not patched') - self.out.writeorg(str) - - def writeorgerr(self, str): - """ write something to the original stderr - """ - if not hasattr(self, 'err'): - raise IOError('stderr not patched') - self.err.writeorg(str) - def callcapture(func, *args, **kwargs): """ call the given function with args/kwargs and return a (res, out, err) tuple where From hpk at codespeak.net Thu Feb 1 15:35:07 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 1 Feb 2007 15:35:07 +0100 (CET) Subject: [py-svn] r37736 - py/trunk/py/io Message-ID: <20070201143507.E72E810097@code0.codespeak.net> Author: hpk Date: Thu Feb 1 15:35:07 2007 New Revision: 37736 Modified: py/trunk/py/io/dupfile.py Log: cosmetic docstring change Modified: py/trunk/py/io/dupfile.py ============================================================================== --- py/trunk/py/io/dupfile.py (original) +++ py/trunk/py/io/dupfile.py Thu Feb 1 15:35:07 2007 @@ -4,8 +4,8 @@ def dupfile(f, mode=None, buffering=0, raising=False): """ return a new open file object that's a duplicate of f - mode is duplicated if not given, buffering controls - buffer size (defaulting to no buffering) and raising + mode is duplicated if not given, 'buffering' controls + buffer size (defaulting to no buffering) and 'raising' defines whether an exception is raised when an incompatible file object is passed in (if raising is False, the file object itself will be returned) From guido at codespeak.net Thu Feb 1 15:55:24 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Thu, 1 Feb 2007 15:55:24 +0100 (CET) Subject: [py-svn] r37738 - py/trunk/py/apigen Message-ID: <20070201145524.4B5F510088@code0.codespeak.net> Author: guido Date: Thu Feb 1 15:55:23 2007 New Revision: 37738 Modified: py/trunk/py/apigen/apigen.py Log: Fixed problem probably caused by removing some comment or something... Modified: py/trunk/py/apigen/apigen.py ============================================================================== --- py/trunk/py/apigen/apigen.py (original) +++ py/trunk/py/apigen/apigen.py Thu Feb 1 15:55:23 2007 @@ -47,7 +47,6 @@ apb.build_namespace_pages(ns_data, proj) capture.err.writeorg('building class pages\n') apb.build_class_pages(class_data, proj) - apb.build_method_pages(method_data, proj) capture.err.writeorg('building function pages\n') apb.build_function_pages(func_data, proj) capture.err.writeorg('building source pages\n') From guido at codespeak.net Thu Feb 1 15:57:36 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Thu, 1 Feb 2007 15:57:36 +0100 (CET) Subject: [py-svn] r37739 - in py/trunk/py/apigen/source: . testing Message-ID: <20070201145736.3D7821008B@code0.codespeak.net> Author: guido Date: Thu Feb 1 15:57:34 2007 New Revision: 37739 Modified: py/trunk/py/apigen/source/html.py py/trunk/py/apigen/source/testing/test_html.py Log: Made that only the first two lines of a source file are examined for the encoding. Modified: py/trunk/py/apigen/source/html.py ============================================================================== --- py/trunk/py/apigen/source/html.py (original) +++ py/trunk/py/apigen/source/html.py Thu Feb 1 15:57:34 2007 @@ -261,8 +261,15 @@ if path[-1] in ['c', 'o']: path = path[:-1] fpath = py.path.local(path) - code = fpath.read() - match = _reg_enc.search(code) + fp = fpath.open() + lines = [] + try: + # encoding is only allowed in the first two lines + for i in range(2): + lines.append(fp.readline()) + finally: + fp.close() + match = _reg_enc.search('\n'.join(lines)) if match: return match.group(1) return 'ISO-8859-1' Modified: py/trunk/py/apigen/source/testing/test_html.py ============================================================================== --- py/trunk/py/apigen/source/testing/test_html.py (original) +++ py/trunk/py/apigen/source/testing/test_html.py Thu Feb 1 15:57:34 2007 @@ -177,3 +177,14 @@ """))) assert get_module_encoding(fpath.strpath) == 'UTF-8' +def test_get_encoding_matching_pattern_elsewhere(): + temp = py.test.ensuretemp('test_get_encoding') + fpath = temp.join('matching_pattern.py') + fpath.write(str(py.code.Source("""\ + #!/usr/bin/env python + + def foo(coding=None): + pass + """))) + assert get_module_encoding(fpath.strpath) == 'ISO-8859-1' + From hpk at codespeak.net Thu Feb 1 16:20:45 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 1 Feb 2007 16:20:45 +0100 (CET) Subject: [py-svn] r37741 - in py/trunk/py: . doc io io/test log/testing misc misc/testing test test/rsession test/rsession/testing test/testing thread/testing Message-ID: <20070201152045.A98601009A@code0.codespeak.net> Author: hpk Date: Thu Feb 1 16:20:39 2007 New Revision: 37741 Added: py/trunk/py/doc/io.txt (contents, props changed) py/trunk/py/io/fdcapture.py - copied, changed from r37733, py/trunk/py/io/capture.py py/trunk/py/io/stdcapture.py - copied, changed from r37733, py/trunk/py/io/capture.py py/trunk/py/io/test/test_simplecapture.py - copied, changed from r37733, py/trunk/py/misc/testing/test_simplecapture.py Removed: py/trunk/py/io/capture.py py/trunk/py/misc/capture.py py/trunk/py/misc/simplecapture.py py/trunk/py/misc/testing/test_simplecapture.py py/trunk/py/test/todo-cleanup.txt Modified: py/trunk/py/__init__.py py/trunk/py/doc/TODO.txt py/trunk/py/doc/index.txt py/trunk/py/io/test/test_capture.py py/trunk/py/log/testing/test_log.py py/trunk/py/test/collect.py py/trunk/py/test/item.py py/trunk/py/test/rsession/local.py py/trunk/py/test/rsession/rsession.py py/trunk/py/test/rsession/testing/test_reporter.py py/trunk/py/test/testing/test_session.py py/trunk/py/thread/testing/test_pool.py Log: monster checking for * unifying IO capturing methods * py.io.StdCapture and py.io.StdCaptureFD (and both have a classmethod 'call' that is a shortcut for capturing output while executing a function) * removing lots of duplicate code * providing some examples in py/doc/io.txt at least tests on win32 and linux seem to pass all for me. Modified: py/trunk/py/__init__.py ============================================================================== --- py/trunk/py/__init__.py (original) +++ py/trunk/py/__init__.py Thu Feb 1 16:20:39 2007 @@ -16,7 +16,7 @@ download_url = "http://codespeak.net/download/py/%s.tar.gz" %(version,), license = "MIT license", platforms = ['unix', 'linux', 'cygwin'], - author = "holger krekel & others", + author = "holger krekel, Armin Rigo, Guido Wesdorp, Maciej Fijalkowski & others", author_email = "py-dev at codespeak.net", long_description = globals()['__doc__'], @@ -94,9 +94,9 @@ # input-output helping 'io.dupfile' : ('./io/dupfile.py', 'dupfile'), - 'io.FDCapture' : ('./io/capture.py', 'FDCapture'), - 'io.OutErrCapture' : ('./io/capture.py', 'OutErrCapture'), - 'io.callcapture' : ('./io/capture.py', 'callcapture'), + 'io.FDCapture' : ('./io/fdcapture.py', 'FDCapture'), + 'io.StdCapture' : ('./io/stdcapture.py', 'StdCapture'), + 'io.StdCaptureFD' : ('./io/stdcapture.py', 'StdCaptureFD'), # error module, defining all errno's as Classes 'error' : ('./misc/error.py', 'error'), Modified: py/trunk/py/doc/TODO.txt ============================================================================== --- py/trunk/py/doc/TODO.txt (original) +++ py/trunk/py/doc/TODO.txt Thu Feb 1 16:20:39 2007 @@ -97,7 +97,7 @@ os.fork to work)) * see why startcapture() used to not use FD-based - "py.io.OutErrCapture" to isolate standard output. + "py.io.StdCaptureFD" to isolate standard output. use that check if all py and PyPy tests pass as good as they do without. Modified: py/trunk/py/doc/index.txt ============================================================================== --- py/trunk/py/doc/index.txt (original) +++ py/trunk/py/doc/index.txt Thu Feb 1 16:20:39 2007 @@ -11,7 +11,9 @@ `py.code`_: High-level access/manipulation of Python code and traceback objects. -`py.xml`_ a fast'n'easy way to generate xml/html documents (including CSS-styling) +`py.xml`_ for generating in-memory xml/html object trees + +`py.io`_ Helper Classes for Capturing of Input/Output `py.log`_ an alpha document about the ad-hoc logging facilities Added: py/trunk/py/doc/io.txt ============================================================================== --- (empty file) +++ py/trunk/py/doc/io.txt Thu Feb 1 16:20:39 2007 @@ -0,0 +1,45 @@ +======= +py.io +======= + +.. contents:: +.. sectnum:: + +The 'py' lib provides helper classes for capturing IO during +execution of a program. + +IO Capturing examples +=============================================== + +:api:`py.io.StdCapture` +--------------------------- + +Basic Example: + + >>> import py + >>> capture = py.io.StdCapture() + >>> print "hello" + >>> out,err = capture.reset() + >>> out.strip() == "hello" + True + +For calling functions you may use a shortcut: + >>> import py + >>> def f(): print "hello" + >>> res, out, err = py.io.StdCapture.call(f) + >>> out.strip() == "hello" + True + +:api:`py.io.StdCaptureFD` +--------------------------- + +If you also want to capture writes to the stdout/stdin +filedescriptors you may invoke: + + >>> import py, sys + >>> capture = py.io.StdCaptureFD() + >>> sys.stdout.write("hello") + >>> sys.stderr.write("world") + >>> out,err = capture.reset() + >>> out.strip() + err.strip() == "helloworld" + True Deleted: /py/trunk/py/io/capture.py ============================================================================== --- /py/trunk/py/io/capture.py Thu Feb 1 16:20:39 2007 +++ (empty file) @@ -1,101 +0,0 @@ - -import os -import sys -import thread -import py - -class FDCapture: - """ Capture IO to/from a given os-level filedescriptor. """ - - def __init__(self, targetfd, tmpfile=None): - self.targetfd = targetfd - if tmpfile is None: - tmpfile = self.maketmpfile() - self.tmpfile = tmpfile - self._savefd = os.dup(targetfd) - os.dup2(self.tmpfile.fileno(), targetfd) - self._patched = [] - - def setasfile(self, name, module=sys): - """ patch . to self.tmpfile - """ - key = (module, name) - self._patched.append((key, getattr(module, name))) - setattr(module, name, self.tmpfile) - - def unsetfiles(self): - """ unpatch all patched items - """ - while self._patched: - (module, name), value = self._patched.pop() - setattr(module, name, value) - - def done(self): - """ unpatch and clean up, returns the self.tmpfile (file object) - """ - os.dup2(self._savefd, self.targetfd) - self.unsetfiles() - os.close(self._savefd) - self.tmpfile.seek(0) - return self.tmpfile - - def maketmpfile(self): - """ create a temporary file - """ - f = os.tmpfile() - newf = py.io.dupfile(f) - f.close() - return newf - - def writeorg(self, str): - """ write a string to the original file descriptor - """ - tempfp = os.tmpfile() - try: - os.dup2(self._savefd, tempfp.fileno()) - tempfp.write(str) - finally: - tempfp.close() - -class OutErrCapture: - """ capture Stdout and Stderr both on filedescriptor - and sys.stdout/stderr level. - """ - def __init__(self, out=True, err=True, patchsys=True): - if out: - self.out = FDCapture(1) - if patchsys: - self.out.setasfile('stdout') - if err: - self.err = FDCapture(2) - if patchsys: - self.err.setasfile('stderr') - - def reset(self): - """ reset sys.stdout and sys.stderr - - returns a tuple of file objects (out, err) for the captured - data - """ - out = err = "" - if hasattr(self, 'out'): - outfile = self.out.done() - out = outfile.read() - if hasattr(self, 'err'): - errfile = self.err.done() - err = errfile.read() - return out, err - -def callcapture(func, *args, **kwargs): - """ call the given function with args/kwargs - and return a (res, out, err) tuple where - out and err represent the output/error output - during function execution. - """ - so = OutErrCapture() - try: - res = func(*args, **kwargs) - finally: - out, err = so.reset() - return res, out, err - Copied: py/trunk/py/io/fdcapture.py (from r37733, py/trunk/py/io/capture.py) ============================================================================== --- py/trunk/py/io/capture.py (original) +++ py/trunk/py/io/fdcapture.py Thu Feb 1 16:20:39 2007 @@ -57,45 +57,3 @@ finally: tempfp.close() -class OutErrCapture: - """ capture Stdout and Stderr both on filedescriptor - and sys.stdout/stderr level. - """ - def __init__(self, out=True, err=True, patchsys=True): - if out: - self.out = FDCapture(1) - if patchsys: - self.out.setasfile('stdout') - if err: - self.err = FDCapture(2) - if patchsys: - self.err.setasfile('stderr') - - def reset(self): - """ reset sys.stdout and sys.stderr - - returns a tuple of file objects (out, err) for the captured - data - """ - out = err = "" - if hasattr(self, 'out'): - outfile = self.out.done() - out = outfile.read() - if hasattr(self, 'err'): - errfile = self.err.done() - err = errfile.read() - return out, err - -def callcapture(func, *args, **kwargs): - """ call the given function with args/kwargs - and return a (res, out, err) tuple where - out and err represent the output/error output - during function execution. - """ - so = OutErrCapture() - try: - res = func(*args, **kwargs) - finally: - out, err = so.reset() - return res, out, err - Copied: py/trunk/py/io/stdcapture.py (from r37733, py/trunk/py/io/capture.py) ============================================================================== --- py/trunk/py/io/capture.py (original) +++ py/trunk/py/io/stdcapture.py Thu Feb 1 16:20:39 2007 @@ -1,73 +1,36 @@ - import os import sys -import thread import py +try: from cStringIO import StringIO +except ImportError: from StringIO import StringIO -class FDCapture: - """ Capture IO to/from a given os-level filedescriptor. """ - - def __init__(self, targetfd, tmpfile=None): - self.targetfd = targetfd - if tmpfile is None: - tmpfile = self.maketmpfile() - self.tmpfile = tmpfile - self._savefd = os.dup(targetfd) - os.dup2(self.tmpfile.fileno(), targetfd) - self._patched = [] - - def setasfile(self, name, module=sys): - """ patch . to self.tmpfile - """ - key = (module, name) - self._patched.append((key, getattr(module, name))) - setattr(module, name, self.tmpfile) - - def unsetfiles(self): - """ unpatch all patched items - """ - while self._patched: - (module, name), value = self._patched.pop() - setattr(module, name, value) - - def done(self): - """ unpatch and clean up, returns the self.tmpfile (file object) - """ - os.dup2(self._savefd, self.targetfd) - self.unsetfiles() - os.close(self._savefd) - self.tmpfile.seek(0) - return self.tmpfile - - def maketmpfile(self): - """ create a temporary file - """ - f = os.tmpfile() - newf = py.io.dupfile(f) - f.close() - return newf - - def writeorg(self, str): - """ write a string to the original file descriptor - """ - tempfp = os.tmpfile() - try: - os.dup2(self._savefd, tempfp.fileno()) - tempfp.write(str) - finally: - tempfp.close() +class Capture(object): + def call(cls, func, *args, **kwargs): + """ return a (res, out, err) tuple where + out and err represent the output/error output + during function execution. + call the given function with args/kwargs + and capture output/error during its execution. + """ + so = cls() + try: + res = func(*args, **kwargs) + finally: + out, err = so.reset() + return res, out, err + call = classmethod(call) -class OutErrCapture: +class StdCaptureFD(Capture): """ capture Stdout and Stderr both on filedescriptor and sys.stdout/stderr level. """ def __init__(self, out=True, err=True, patchsys=True): if out: - self.out = FDCapture(1) + self.out = py.io.FDCapture(1) if patchsys: self.out.setasfile('stdout') if err: - self.err = FDCapture(2) + self.err = py.io.FDCapture(2) if patchsys: self.err.setasfile('stderr') @@ -86,16 +49,44 @@ err = errfile.read() return out, err -def callcapture(func, *args, **kwargs): - """ call the given function with args/kwargs - and return a (res, out, err) tuple where - out and err represent the output/error output - during function execution. - """ - so = OutErrCapture() - try: - res = func(*args, **kwargs) - finally: - out, err = so.reset() - return res, out, err +class StdCapture(Capture): + """ capture sys.stdout/sys.stderr (but not system level fd 1 and 2). + this captures only "In-Memory" and is currently intended to be + used by the unittest package to capture print-statements in tests. + """ + def __init__(self): + self.oldin = sys.stdin + self.oldout = sys.stdout + self.olderr = sys.stderr + sys.stdin = self.newin = DontReadFromInput() + sys.stdout = self.newout = StringIO() + sys.stderr = self.newerr = StringIO() + + def reset(self): + """ return captured output and restore sys.stdout/err.""" + x, y = self.done() + return x.read(), y.read() + + def done(self): + o,e = sys.stdout, sys.stderr + sys.stdin, sys.stdout, sys.stderr = ( + self.oldin, self.oldout, self.olderr) + del self.oldin, self.oldout, self.olderr + o, e = self.newout, self.newerr + o.seek(0) + e.seek(0) + return o,e + +class DontReadFromInput: + """Temporary stub class. Ideally when stdin is accessed, the + capturing should be turned off, with possibly all data captured + so far sent to the screen. This should be configurable, though, + because in automated test runs it is better to crash than + hang indefinitely. + """ + def read(self, *args): + raise IOError("reading from stdin while output is captured") + readline = read + readlines = read + __iter__ = read Modified: py/trunk/py/io/test/test_capture.py ============================================================================== --- py/trunk/py/io/test/test_capture.py (original) +++ py/trunk/py/io/test/test_capture.py Thu Feb 1 16:20:39 2007 @@ -59,7 +59,7 @@ class TestCapturing: def getcapture(self): - return py.io.OutErrCapture() + return py.io.StdCaptureFD() def test_capturing_simple(self): cap = self.getcapture() @@ -120,13 +120,13 @@ print >>py.std.sys.stderr, y return 42 - res, out, err = py.io.callcapture(func, 3, y=4) + res, out, err = py.io.StdCaptureFD.call(func, 3, y=4) assert res == 42 assert out.startswith("3") assert err.startswith("4") def test_just_out_capture(): - cap = py.io.OutErrCapture(out=True, err=False) + cap = py.io.StdCaptureFD(out=True, err=False) print >>sys.stdout, "hello" print >>sys.stderr, "world" out, err = cap.reset() @@ -134,7 +134,7 @@ assert not err def test_just_err_capture(): - cap = py.io.OutErrCapture(out=False, err=True) + cap = py.io.StdCaptureFD(out=False, err=True) print >>sys.stdout, "hello" print >>sys.stderr, "world" out, err = cap.reset() @@ -142,7 +142,7 @@ assert not out def test_capture_no_sys(): - cap = py.io.OutErrCapture(patchsys=False) + cap = py.io.StdCaptureFD(patchsys=False) print >>sys.stdout, "hello" print >>sys.stderr, "world" os.write(1, "1") Copied: py/trunk/py/io/test/test_simplecapture.py (from r37733, py/trunk/py/misc/testing/test_simplecapture.py) ============================================================================== --- py/trunk/py/misc/testing/test_simplecapture.py (original) +++ py/trunk/py/io/test/test_simplecapture.py Thu Feb 1 16:20:39 2007 @@ -1,29 +1,10 @@ import os, sys import py -from py.__.misc.simplecapture import SimpleOutErrCapture, callcapture -from py.__.misc.capture import Capture, FDCapture - -class TestFDCapture: - def test_basic(self): - tmpfile = py.std.os.tmpfile() - fd = tmpfile.fileno() - cap = FDCapture(fd) - os.write(fd, "hello") - f = cap.done() - s = f.read() - assert s == "hello" - - def test_stderr(self): - cap = FDCapture(2, 'stderr') - print >>sys.stderr, "hello" - f = cap.done() - s = f.read() - assert s == "hello\n" class TestCapturingOnSys: def getcapture(self): - return SimpleOutErrCapture() + return py.io.StdCapture() def test_capturing_simple(self): cap = self.getcapture() @@ -73,20 +54,14 @@ finally: cap.reset() -def test_callcapture(): +def test_callcapture_nofd(): def func(x, y): print x print >>py.std.sys.stderr, y return 42 - res, out, err = callcapture(func, 3, y=4) + res, out, err = py.io.StdCapture.call(func, 3, y=4) assert res == 42 assert out.startswith("3") assert err.startswith("4") -class TestCapturingOnFDs(TestCapturingOnSys): - def test_reading_stdin_while_captured_doesnt_hang(self): - py.test.skip("Hangs in py.test --session=R") - - def getcapture(self): - return Capture() Modified: py/trunk/py/log/testing/test_log.py ============================================================================== --- py/trunk/py/log/testing/test_log.py (original) +++ py/trunk/py/log/testing/test_log.py Thu Feb 1 16:20:39 2007 @@ -1,7 +1,8 @@ import py -from py.__.misc.simplecapture import callcapture import sys +callcapture = py.io.StdCapture.call + def setup_module(mod): mod.tempdir = py.test.ensuretemp("py.log-test") mod.logstate = py.log._getstate() Deleted: /py/trunk/py/misc/capture.py ============================================================================== --- /py/trunk/py/misc/capture.py Thu Feb 1 16:20:39 2007 +++ (empty file) @@ -1,49 +0,0 @@ - -import os, sys - -class FDCapture: - def __init__(self, targetfd, sysattr=None): - self.targetfd = targetfd - self.tmpfile = self.maketmpfile() - self._savefd = os.dup(targetfd) - os.dup2(self.tmpfile.fileno(), targetfd) - if sysattr is not None: - self._reset = (lambda oldval=getattr(sys, sysattr): - setattr(sys, sysattr, oldval)) - setattr(sys, sysattr, self.tmpfile) - - def done(self): - os.dup2(self._savefd, self.targetfd) - if hasattr(self, '_reset'): - self._reset() - del self._reset - os.close(self._savefd) - f = self.tmpfile - f.seek(0) - del self._savefd - del self.tmpfile - return f - - def maketmpfile(self): - f = os.tmpfile() - fd = f.fileno() - newfd = os.dup(fd) - newf = os.fdopen(newfd, 'w+b', 0) - f.close() - return newf - -class Capture: - def __init__(self): - self._out = FDCapture(1, 'stdout') - self._oldsysout = sys.stdout - sys.stdout = self._out.tmpfile - - self._err = FDCapture(2, 'stderr') - self._olderrout = sys.stderr - sys.stderr = self._err.tmpfile - - def reset(self): - outfile = self._out.done() - errfile = self._err.done() - return outfile.read(), errfile.read() - Deleted: /py/trunk/py/misc/simplecapture.py ============================================================================== --- /py/trunk/py/misc/simplecapture.py Thu Feb 1 16:20:39 2007 +++ (empty file) @@ -1,58 +0,0 @@ -""" - -capture stdout/stderr - -""" -import sys -try: from cStringIO import StringIO -except ImportError: from StringIO import StringIO - -class SimpleOutErrCapture: - """ capture sys.stdout/sys.stderr (but not system level fd 1 and 2). - - this captures only "In-Memory" and is currently intended to be - used by the unittest package to capture print-statements in tests. - """ - def __init__(self): - self.oldin = sys.stdin - self.oldout = sys.stdout - self.olderr = sys.stderr - sys.stdin = self.newin = DontReadFromInput() - sys.stdout = self.newout = StringIO() - sys.stderr = self.newerr = StringIO() - - def reset(self): - """ return captured output and restore sys.stdout/err.""" - x, y = self.done() - return x.read(), y.read() - - def done(self): - o,e = sys.stdout, sys.stderr - sys.stdin, sys.stdout, sys.stderr = ( - self.oldin, self.oldout, self.olderr) - del self.oldin, self.oldout, self.olderr - o, e = self.newout, self.newerr - o.seek(0) - e.seek(0) - return o,e - -class DontReadFromInput: - """Temporary stub class. Ideally when stdin is accessed, the - capturing should be turned off, with possibly all data captured - so far sent to the screen. This should be configurable, though, - because in automated test runs it is better to crash than - hang indefinitely. - """ - def read(self, *args): - raise IOError("reading from stdin while output is captured") - readline = read - readlines = read - __iter__ = read - -def callcapture(func, *args, **kwargs): - so = SimpleOutErrCapture() - try: - res = func(*args, **kwargs) - finally: - out, err = so.reset() - return res, out, err Deleted: /py/trunk/py/misc/testing/test_simplecapture.py ============================================================================== --- /py/trunk/py/misc/testing/test_simplecapture.py Thu Feb 1 16:20:39 2007 +++ (empty file) @@ -1,92 +0,0 @@ -import os, sys -import py -from py.__.misc.simplecapture import SimpleOutErrCapture, callcapture -from py.__.misc.capture import Capture, FDCapture - -class TestFDCapture: - def test_basic(self): - tmpfile = py.std.os.tmpfile() - fd = tmpfile.fileno() - cap = FDCapture(fd) - os.write(fd, "hello") - f = cap.done() - s = f.read() - assert s == "hello" - - def test_stderr(self): - cap = FDCapture(2, 'stderr') - print >>sys.stderr, "hello" - f = cap.done() - s = f.read() - assert s == "hello\n" - -class TestCapturingOnSys: - - def getcapture(self): - return SimpleOutErrCapture() - - def test_capturing_simple(self): - cap = self.getcapture() - print "hello world" - print >>sys.stderr, "hello error" - out, err = cap.reset() - assert out == "hello world\n" - assert err == "hello error\n" - - def test_capturing_twice_error(self): - cap = self.getcapture() - print "hello" - cap.reset() - py.test.raises(AttributeError, "cap.reset()") - - def test_capturing_modify_sysouterr_in_between(self): - oldout = sys.stdout - olderr = sys.stderr - cap = self.getcapture() - print "hello", - print >>sys.stderr, "world", - sys.stdout = py.std.StringIO.StringIO() - sys.stderr = py.std.StringIO.StringIO() - print "not seen" - print >>sys.stderr, "not seen" - out, err = cap.reset() - assert out == "hello" - assert err == "world" - assert sys.stdout == oldout - assert sys.stderr == olderr - - def test_capturing_error_recursive(self): - cap1 = self.getcapture() - print "cap1" - cap2 = self.getcapture() - print "cap2" - out2, err2 = cap2.reset() - py.test.raises(AttributeError, "cap2.reset()") - out1, err1 = cap1.reset() - assert out1 == "cap1\n" - assert out2 == "cap2\n" - - def test_reading_stdin_while_captured_doesnt_hang(self): - cap = self.getcapture() - try: - py.test.raises(IOError, raw_input) - finally: - cap.reset() - -def test_callcapture(): - def func(x, y): - print x - print >>py.std.sys.stderr, y - return 42 - - res, out, err = callcapture(func, 3, y=4) - assert res == 42 - assert out.startswith("3") - assert err.startswith("4") - -class TestCapturingOnFDs(TestCapturingOnSys): - def test_reading_stdin_while_captured_doesnt_hang(self): - py.test.skip("Hangs in py.test --session=R") - - def getcapture(self): - return Capture() Modified: py/trunk/py/test/collect.py ============================================================================== --- py/trunk/py/test/collect.py (original) +++ py/trunk/py/test/collect.py Thu Feb 1 16:20:39 2007 @@ -379,11 +379,7 @@ def startcapture(self): if not self.config.option.nocapture: assert not hasattr(self, '_capture') - self._capture = py.io.OutErrCapture() - # XXX integrate this into py.io / refactor - # execnet/py.test capturing mechanisms - #from py.__.misc.simplecapture import SimpleOutErrCapture - #self._capture = SimpleOutErrCapture() + self._capture = py.io.StdCaptureFD() def finishcapture(self): if hasattr(self, '_capture'): Modified: py/trunk/py/test/item.py ============================================================================== --- py/trunk/py/test/item.py (original) +++ py/trunk/py/test/item.py Thu Feb 1 16:20:39 2007 @@ -32,10 +32,7 @@ class Item(py.test.collect.Collector): def startcapture(self): if not self.config.option.nocapture: - # XXX refactor integrate capturing - self._capture = py.io.OutErrCapture() - #from py.__.misc.simplecapture import SimpleOutErrCapture - #self._capture = SimpleOutErrCapture() + self._capture = py.io.StdCaptureFD() def finishcapture(self): if hasattr(self, '_capture'): Modified: py/trunk/py/test/rsession/local.py ============================================================================== --- py/trunk/py/test/rsession/local.py (original) +++ py/trunk/py/test/rsession/local.py Thu Feb 1 16:20:39 2007 @@ -2,6 +2,7 @@ """ local-only operations """ +import py from py.__.test.rsession.executor import BoxExecutor, RunExecutor,\ ApigenExecutor from py.__.test.rsession import report @@ -9,10 +10,8 @@ # XXX copied from session.py def startcapture(session): - if not session.config.option.nocapture and not session.config.option.usepdb: - # XXX refactor integrate capturing - from py.__.misc.simplecapture import SimpleOutErrCapture - session._capture = SimpleOutErrCapture() + if not session.config.option.nocapture: + session._capture = py.io.StdCapture() def finishcapture(session): if hasattr(session, '_capture'): Modified: py/trunk/py/test/rsession/rsession.py ============================================================================== --- py/trunk/py/test/rsession/rsession.py (original) +++ py/trunk/py/test/rsession/rsession.py Thu Feb 1 16:20:39 2007 @@ -259,7 +259,7 @@ raise NotImplementedError("%s does not contain 'build' " "function" %(apigen,)) print >>sys.stderr, 'building documentation' - capture = py.io.OutErrCapture() + capture = py.io.StdCaptureFD() try: pkgdir = self.getpkgdir(self.config.args[0]) apigen.build(pkgdir, Modified: py/trunk/py/test/rsession/testing/test_reporter.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_reporter.py (original) +++ py/trunk/py/test/rsession/testing/test_reporter.py Thu Feb 1 16:20:39 2007 @@ -74,7 +74,7 @@ for outcome in outcomes: r.report(report.ReceivedItemOutcome(ch, item, outcome)) - cap = py.io.OutErrCapture() + cap = py.io.StdCaptureFD() boxfun(config, item, outcomes) out, err = cap.reset() assert not err @@ -97,7 +97,7 @@ for outcome in outcomes: r.report(report.ReceivedItemOutcome(ch, funcitem, outcome)) - cap = py.io.OutErrCapture() + cap = py.io.StdCaptureFD() boxfun(self.pkgdir, config, moditem, funcitem, outcomes) out, err = cap.reset() assert not err @@ -125,7 +125,7 @@ r = self.reporter(config, hosts) list(rootcol.tryiter(reporterror=lambda x : AbstractSession.reporterror(r.report, x))) - cap = py.io.OutErrCapture() + cap = py.io.StdCaptureFD() boxfun() out, err = cap.reset() assert not err @@ -147,7 +147,7 @@ list(rootcol.tryiter(reporterror=lambda x : AbstractSession.reporterror(r.report, x))) r.report(report.TestFinished()) - cap = py.io.OutErrCapture() + cap = py.io.StdCaptureFD() boxfun() out, err = cap.reset() assert not err @@ -156,7 +156,7 @@ def _test_still_to_go(self): tmpdir = py.test.ensuretemp("stilltogo") tmpdir.ensure("__init__.py") - cap = py.io.OutErrCapture() + cap = py.io.StdCaptureFD() config = py.test.config._reparse([str(tmpdir)]) hosts = [HostInfo(i) for i in ["host1", "host2", "host3"]] r = self.reporter(config, hosts) Modified: py/trunk/py/test/testing/test_session.py ============================================================================== --- py/trunk/py/test/testing/test_session.py (original) +++ py/trunk/py/test/testing/test_session.py Thu Feb 1 16:20:39 2007 @@ -189,7 +189,7 @@ import py class Function(py.test.Function): def startcapture(self): - self._mycapture = py.io.OutErrCapture() + self._mycapture = py.io.StdCaptureFD() def finishcapture(self): self._testmycapture = self._mycapture.reset() Deleted: /py/trunk/py/test/todo-cleanup.txt ============================================================================== --- /py/trunk/py/test/todo-cleanup.txt Thu Feb 1 16:20:39 2007 +++ (empty file) @@ -1,10 +0,0 @@ - - -fix --looponfailing: currently in case of failures -all tests are re-run. the problem was introduced -while cleaning up session.main() calls ... -the setup of remote and local config objects -and collectors needs to be reviewed anyway -(and the terminalsession and rsession handling -of such config object should be unified with -respect to this configuration/failure communication) Modified: py/trunk/py/thread/testing/test_pool.py ============================================================================== --- py/trunk/py/thread/testing/test_pool.py (original) +++ py/trunk/py/thread/testing/test_pool.py Thu Feb 1 16:20:39 2007 @@ -77,7 +77,7 @@ pool.join(timeout=0.1) def test_pool_clean_shutdown(): - capture = py.io.OutErrCapture() + capture = py.io.StdCaptureFD() pool = WorkerPool() def f(): pass From hpk at codespeak.net Thu Feb 1 16:23:29 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 1 Feb 2007 16:23:29 +0100 (CET) Subject: [py-svn] r37742 - py/trunk/py/doc Message-ID: <20070201152329.A9446100A4@code0.codespeak.net> Author: hpk Date: Thu Feb 1 16:23:27 2007 New Revision: 37742 Modified: py/trunk/py/doc/index.txt Log: fix reference Modified: py/trunk/py/doc/index.txt ============================================================================== --- py/trunk/py/doc/index.txt (original) +++ py/trunk/py/doc/index.txt Thu Feb 1 16:23:27 2007 @@ -35,6 +35,7 @@ .. _`py.execnet`: execnet.html .. _`py.magic.greenlet`: greenlet.html .. _`py.log`: log.html +.. _`py.io`: io.html .. _`py.path`: path.html .. _`py.code`: code.html .. _`py.test`: test.html From hpk at codespeak.net Thu Feb 1 16:23:52 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 1 Feb 2007 16:23:52 +0100 (CET) Subject: [py-svn] r37743 - py/trunk/py/misc Message-ID: <20070201152352.58F3A10090@code0.codespeak.net> Author: hpk Date: Thu Feb 1 16:23:51 2007 New Revision: 37743 Removed: py/trunk/py/misc/stdoutcapture.py Log: ah, there was yet another version of capturing hiding Deleted: /py/trunk/py/misc/stdoutcapture.py ============================================================================== --- /py/trunk/py/misc/stdoutcapture.py Thu Feb 1 16:23:51 2007 +++ (empty file) @@ -1,73 +0,0 @@ -""" -A quick hack to capture stdout/stderr. -""" - -import os, sys - - -class Capture: - - def __init__(self, mixed_out_err = False): - "Start capture of the Unix-level stdout and stderr." - if (not hasattr(os, 'tmpfile') or - not hasattr(os, 'dup') or - not hasattr(os, 'dup2') or - not hasattr(os, 'fdopen')): - self.dummy = 1 - else: - self.dummy = 0 - # make new stdout/stderr files if needed - self.localoutfd = os.dup(1) - self.localerrfd = os.dup(2) - if hasattr(sys.stdout, 'fileno') and sys.stdout.fileno() == 1: - self.saved_stdout = sys.stdout - sys.stdout = os.fdopen(self.localoutfd, 'w', 1) - else: - self.saved_stdout = None - if hasattr(sys.stderr, 'fileno') and sys.stderr.fileno() == 2: - self.saved_stderr = sys.stderr - sys.stderr = os.fdopen(self.localerrfd, 'w', 0) - else: - self.saved_stderr = None - self.tmpout = os.tmpfile() - if mixed_out_err: - self.tmperr = self.tmpout - else: - self.tmperr = os.tmpfile() - os.dup2(self.tmpout.fileno(), 1) - os.dup2(self.tmperr.fileno(), 2) - - def done(self): - "End capture and return the captured text (stdoutfile, stderrfile)." - if self.dummy: - import cStringIO - return cStringIO.StringIO(), cStringIO.StringIO() - else: - os.dup2(self.localoutfd, 1) - os.dup2(self.localerrfd, 2) - if self.saved_stdout is not None: - f = sys.stdout - sys.stdout = self.saved_stdout - f.close() - else: - os.close(self.localoutfd) - if self.saved_stderr is not None: - f = sys.stderr - sys.stderr = self.saved_stderr - f.close() - else: - os.close(self.localerrfd) - self.tmpout.seek(0) - self.tmperr.seek(0) - return self.tmpout, self.tmperr - - -if __name__ == '__main__': - # test - c = Capture() - try: - os.system('echo hello') - finally: - fout, ferr = c.done() - print 'Output:', `fout.read()` - print 'Error:', `ferr.read()` From hpk at codespeak.net Thu Feb 1 16:35:30 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 1 Feb 2007 16:35:30 +0100 (CET) Subject: [py-svn] r37745 - py/trunk/py/io Message-ID: <20070201153530.06544100A4@code0.codespeak.net> Author: hpk Date: Thu Feb 1 16:35:29 2007 New Revision: 37745 Modified: py/trunk/py/io/stdcapture.py Log: fixing and adding to docstring Modified: py/trunk/py/io/stdcapture.py ============================================================================== --- py/trunk/py/io/stdcapture.py (original) +++ py/trunk/py/io/stdcapture.py Thu Feb 1 16:35:29 2007 @@ -52,8 +52,8 @@ class StdCapture(Capture): """ capture sys.stdout/sys.stderr (but not system level fd 1 and 2). - this captures only "In-Memory" and is currently intended to be - used by the unittest package to capture print-statements in tests. + This class allows to capture writes to sys.stdout|stderr "in-memory" + and will raise errors on tries to read from sys.stdin. """ def __init__(self): self.oldin = sys.stdin From hpk at codespeak.net Thu Feb 1 16:58:11 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 1 Feb 2007 16:58:11 +0100 (CET) Subject: [py-svn] r37751 - in py/trunk/py: c-extension/greenlet misc Message-ID: <20070201155811.9D5BB10079@code0.codespeak.net> Author: hpk Date: Thu Feb 1 16:58:10 2007 New Revision: 37751 Modified: py/trunk/py/c-extension/greenlet/test_generator.py py/trunk/py/misc/buildcmodule.py Log: fix two other places that used capturing (although the greenlet fix is not really related, but i first saw it now on win32) Modified: py/trunk/py/c-extension/greenlet/test_generator.py ============================================================================== --- py/trunk/py/c-extension/greenlet/test_generator.py (original) +++ py/trunk/py/c-extension/greenlet/test_generator.py Thu Feb 1 16:58:10 2007 @@ -1,7 +1,7 @@ import py try: from py.magic import greenlet -except RuntimeError, e: +except (ImportError, RuntimeError), e: py.test.skip(str(e)) Modified: py/trunk/py/misc/buildcmodule.py ============================================================================== --- py/trunk/py/misc/buildcmodule.py (original) +++ py/trunk/py/misc/buildcmodule.py Thu Feb 1 16:58:10 2007 @@ -11,7 +11,6 @@ import os, sys, imp from distutils.core import setup from distutils.extension import Extension - import stdoutcapture debug = 0 #try: @@ -36,7 +35,7 @@ if lib.check(): lib.remove() - c = stdoutcapture.Capture(mixed_out_err = True) + c = py.io.StdCaptureFD() try: try: saved_environ = os.environ.items() From guido at codespeak.net Thu Feb 1 16:58:43 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Thu, 1 Feb 2007 16:58:43 +0100 (CET) Subject: [py-svn] r37752 - py/trunk/py/doc Message-ID: <20070201155843.739F41008D@code0.codespeak.net> Author: guido Date: Thu Feb 1 16:58:42 2007 New Revision: 37752 Modified: py/trunk/py/doc/path.txt Log: Added some more code examples. Modified: py/trunk/py/doc/path.txt ============================================================================== --- py/trunk/py/doc/path.txt (original) +++ py/trunk/py/doc/path.txt Thu Feb 1 16:58:42 2007 @@ -8,7 +8,8 @@ The 'py' lib provides a uniform high-level api to deal with filesystems and filesystem-like interfaces: :api:`py.path`. It aims to offer a central object to fs-like object trees (reading from and writing to files, adding -files/directories, examining the types and structure, etc.). +files/directories, examining the types and structure, etc.), and out-of-the-box +provides a number of implementations of this API. Path implementations provided by :api:`py.path` =============================================== @@ -34,6 +35,9 @@ True >>> foopath.read() 'bar' + >>> foofile = foopath.open() # return a 'real' file object + >>> foofile.read(1) + 'b' :api:`py.path.svnurl` and :api:`py.path.svnwc` ---------------------------------------------- @@ -74,32 +78,103 @@ path object within an application (e.g. from "local" to "svnwc"). The common set includes functions such as `path.read()` to read all data from a file, `path.write()` to write data, `path.listdir()` to get a list -of directory entries, and `path.check()` to check if a node exists +of directory entries, `path.check()` to check if a node exists and is of a particular type, `path.join()` to get to a (grand)child, `path.visit()` to recursively walk through a node's -children, etc. Only things that are not common on all filesystems, such -as handling metadata (e.g. the subversion "properties") require +children, etc. Only things that are not common on 'normal' filesystems (yet), +such as handling metadata (e.g. the Subversion "properties") require using specific APIs. Examples --------------------------------- +A quick 'cookbook' of small examples that will be useful 'in real life', +which also presents parts of the 'common' API, and shows some non-common +methods: + Searching `.txt` files +++++++++++++++++++++++++++++++++++++ -XXX example +Search for a particular string inside all files with a .txt extension in a +specific directory. +:: -Joining and checking path types -+++++++++++++++++++++++++++++++++++++ + >>> dirpath = temppath.ensure('testdir', dir=True) + >>> dirpath.join('textfile1.txt').write('foo bar baz') + >>> dirpath.join('textfile2.txt').write('frob bar spam eggs') + >>> subdir = dirpath.ensure('subdir', dir=True) + >>> subdir.join('textfile1.txt').write('foo baz') + >>> subdir.join('textfile2.txt').write('spam eggs spam foo bar spam') + >>> results = [] + >>> for fpath in dirpath.visit('*.txt'): + ... if 'bar' in fpath.read(): + ... results.append(fpath.relto(dirpath)) + >>> results + ['textfile1.txt', 'textfile2.txt', 'subdir/textfile2.txt'] + +Joining path types +++++++++++++++++++++ + +This example shows the :api:`py.path` features to deal with actual paths +(strings). Note that the filesystem is never touched, all operations are +performed on a string level (so the paths don't have to exist, either):: + + >>> p1 = py.path.local('/foo/bar') + >>> p2 = p1.join('baz/qux') + >>> p2.strpath + '/foo/bar/baz/qux' + >>> p2.relto(p1) + 'baz/qux' + >>> p3 = p1 / 'baz/qux' # the / operator allows joining, too + >>> p2 == p3 + True + +This should be possible on every implementation of :api:`py.path`, so +regardless of whether the implementation wraps a UNIX filesystem, a Windows +one, or a database or object tree, these functions should be available (each +with their own notion of path seperators and dealing with conversions, etc.). -XXX example +Checking path types ++++++++++++++++++++++ -setting svn-properties +Now we will show a bit about the powerful 'check()' method on paths, which +allows you to check whether a file exists, what type it is, etc.:: + + >>> file1 = temppath.join('file1') + >>> file1.check() # does it exist? + False + >>> file1 = file1.ensure(file=True) # 'touch' the file + >>> file1.check() + True + >>> file1.check(dir=True) # is it a dir? + False + >>> file1.check(file=True) # or a file? + True + >>> file1.check(ext='.txt') # check the extension + False + >>> textfile = temppath.ensure('text.txt', file=True) + >>> textfile.check(ext='.txt') + True + >>> file1.check(basename='file1') # we can use all the path's properties here + True + +Setting svn-properties +++++++++++++++++++++++++++++++++++++++ -XXX example +As an example of 'uncommon' methods, we'll show how to read and write +properties in an :api:`py.path.svnwc` instance:: + >>> wc.propget('foo') + '' + >>> wc.propset('foo', 'bar') + >>> wc.propget('foo') + 'bar' + >>> len(wc.status().prop_modified) # our own props + 1 + >>> msg = wc.revert() # roll back our changes + >>> len(wc.status().prop_modified) + 0 XXX more examples (look at API) +++++++++++++++++++++++++++++++++++++++ @@ -117,7 +192,6 @@ XXX note more here - Future plans ============ From guido at codespeak.net Thu Feb 1 21:10:53 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Thu, 1 Feb 2007 21:10:53 +0100 (CET) Subject: [py-svn] r37765 - py/trunk/py/apigen Message-ID: <20070201201053.532C010074@code0.codespeak.net> Author: guido Date: Thu Feb 1 21:10:48 2007 New Revision: 37765 Modified: py/trunk/py/apigen/apigen.py py/trunk/py/apigen/htmlgen.py Log: Was still getting filenames for source files from code objects, so adding more defensiveness, and made that the 'capture' object is passed over to the builder instances (to help debugging, currently not used). Modified: py/trunk/py/apigen/apigen.py ============================================================================== --- py/trunk/py/apigen/apigen.py (original) +++ py/trunk/py/apigen/apigen.py Thu Feb 1 21:10:48 2007 @@ -29,8 +29,9 @@ all_names = dsa._get_names(filter=lambda x, y: True) namespace_tree = htmlgen.create_namespace_tree(all_names) - apb = htmlgen.ApiPageBuilder(targetdir, l, dsa, pkgdir, namespace_tree) - spb = htmlgen.SourcePageBuilder(targetdir, l, pkgdir) + apb = htmlgen.ApiPageBuilder(targetdir, l, dsa, pkgdir, namespace_tree, + capture) + spb = htmlgen.SourcePageBuilder(targetdir, l, pkgdir, capture) capture.err.writeorg('preparing namespace pages\n') ns_data = apb.prepare_namespace_pages() Modified: py/trunk/py/apigen/htmlgen.py ============================================================================== --- py/trunk/py/apigen/htmlgen.py (original) +++ py/trunk/py/apigen/htmlgen.py Thu Feb 1 21:10:48 2007 @@ -205,10 +205,11 @@ class SourcePageBuilder(AbstractPageBuilder): """ builds the html for a source docs page """ - def __init__(self, base, linker, projroot): + def __init__(self, base, linker, projroot, capture=None): self.base = base self.linker = linker self.projroot = projroot + self.capture = capture def build_navigation(self, fspath): nav = H.Navigation() @@ -348,14 +349,16 @@ class ApiPageBuilder(AbstractPageBuilder): """ builds the html for an api docs page """ - def __init__(self, base, linker, dsa, projroot, namespace_tree): + def __init__(self, base, linker, dsa, projroot, namespace_tree, + capture=None): self.base = base self.linker = linker self.dsa = dsa self.projroot = projroot self.projpath = py.path.local(projroot) self.namespace_tree = namespace_tree - + self.capture = capture + def build_callable_view(self, dotted_name): """ build the html for a class method """ # XXX we may want to have seperate @@ -728,7 +731,8 @@ # skip py.code.Source objects and source files outside of the # package is_code_source = self._reg_source.match(sourcefile) - if (not is_code_source and self.is_in_pkg(sourcefile)): + if (not is_code_source and self.is_in_pkg(sourcefile) and + py.path.local(sourcefile).check()): enc = source_html.get_module_encoding(sourcefile) href = self.linker.get_lazyhref(sourcefile) sourcelink = H.a(linktext, href=href) @@ -737,7 +741,8 @@ sourcelink = H.div(linktext) colored = enumerate_and_color(mangled, frame.firstlineno, enc) else: - sourcelink = H.div('source unknown') + sourcelink = H.div('source unknown (%s)' % (sourcefile,)) + colored = mangled[:] tbdiv.append(sourcelink) tbdiv.append(H.div(class_='code', *colored)) return tbdiv From hpk at codespeak.net Thu Feb 1 21:26:28 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 1 Feb 2007 21:26:28 +0100 (CET) Subject: [py-svn] r37766 - in py/trunk/py/io: . test Message-ID: <20070201202628.1197C1007F@code0.codespeak.net> Author: hpk Date: Thu Feb 1 21:26:27 2007 New Revision: 37766 Removed: py/trunk/py/io/test/test_simplecapture.py Modified: py/trunk/py/io/stdcapture.py py/trunk/py/io/test/test_capture.py Log: unifying non-FD and FD capturing some more (could be more, but at least the APIs start to feel the same) Modified: py/trunk/py/io/stdcapture.py ============================================================================== --- py/trunk/py/io/stdcapture.py (original) +++ py/trunk/py/io/stdcapture.py Thu Feb 1 21:26:27 2007 @@ -4,6 +4,8 @@ try: from cStringIO import StringIO except ImportError: from StringIO import StringIO +emptyfile = StringIO() + class Capture(object): def call(cls, func, *args, **kwargs): """ return a (res, out, err) tuple where @@ -24,13 +26,17 @@ """ capture Stdout and Stderr both on filedescriptor and sys.stdout/stderr level. """ - def __init__(self, out=True, err=True, patchsys=True): + def __init__(self, out=True, err=True, mixed=False, patchsys=True): if out: self.out = py.io.FDCapture(1) if patchsys: self.out.setasfile('stdout') if err: - self.err = py.io.FDCapture(2) + if mixed and out: + tmpfile = self.out.tmpfile + else: + tmpfile = None + self.err = py.io.FDCapture(2, tmpfile=tmpfile) if patchsys: self.err.setasfile('stderr') @@ -40,14 +46,12 @@ returns a tuple of file objects (out, err) for the captured data """ - out = err = "" + outfile = errfile = emptyfile if hasattr(self, 'out'): outfile = self.out.done() - out = outfile.read() if hasattr(self, 'err'): errfile = self.err.done() - err = errfile.read() - return out, err + return outfile.read(), errfile.read() class StdCapture(Capture): """ capture sys.stdout/sys.stderr (but not system level fd 1 and 2). @@ -55,13 +59,21 @@ This class allows to capture writes to sys.stdout|stderr "in-memory" and will raise errors on tries to read from sys.stdin. """ - def __init__(self): + def __init__(self, out=True, err=True, mixed=False): + self._out = out + self._err = err + if out: + self.oldout = sys.stdout + sys.stdout = self.newout = StringIO() + if err: + self.olderr = sys.stderr + if out and mixed: + newerr = self.newout + else: + newerr = StringIO() + sys.stderr = self.newerr = newerr self.oldin = sys.stdin - self.oldout = sys.stdout - self.olderr = sys.stderr sys.stdin = self.newin = DontReadFromInput() - sys.stdout = self.newout = StringIO() - sys.stderr = self.newerr = StringIO() def reset(self): """ return captured output and restore sys.stdout/err.""" @@ -70,13 +82,25 @@ def done(self): o,e = sys.stdout, sys.stderr - sys.stdin, sys.stdout, sys.stderr = ( - self.oldin, self.oldout, self.olderr) - del self.oldin, self.oldout, self.olderr - o, e = self.newout, self.newerr - o.seek(0) - e.seek(0) - return o,e + outfile = errfile = emptyfile + if self._out: + try: + sys.stdout = self.oldout + except AttributeError: + raise IOError("stdout capturing already reset") + del self.oldout + outfile = self.newout + outfile.seek(0) + if self._err: + try: + sys.stderr = self.olderr + except AttributeError: + raise IOError("stderr capturing already reset") + del self.olderr + errfile = self.newerr + errfile.seek(0) + sys.stdin = self.oldin + return outfile, errfile class DontReadFromInput: """Temporary stub class. Ideally when stdin is accessed, the Modified: py/trunk/py/io/test/test_capture.py ============================================================================== --- py/trunk/py/io/test/test_capture.py (original) +++ py/trunk/py/io/test/test_capture.py Thu Feb 1 21:26:27 2007 @@ -57,9 +57,9 @@ tmpfp.close() f = cap.done() -class TestCapturing: - def getcapture(self): - return py.io.StdCaptureFD() +class TestStdCapture: + def getcapture(self, **kw): + return py.io.StdCapture(**kw) def test_capturing_simple(self): cap = self.getcapture() @@ -69,6 +69,15 @@ assert out == "hello world\n" assert err == "hello error\n" + def test_capturing_mixed(self): + cap = self.getcapture(mixed=True) + print "hello", + print >>sys.stderr, "world", + print >>sys.stdout, ".", + out, err = cap.reset() + assert out.strip() == "hello world ." + assert not err + def test_capturing_twice_error(self): cap = self.getcapture() print "hello" @@ -101,6 +110,26 @@ out1, err1 = cap1.reset() assert out1 == "cap1\n" assert out2 == "cap2\n" + + def test_just_out_capture(self): + cap = self.getcapture(out=True, err=False) + print >>sys.stdout, "hello" + print >>sys.stderr, "world" + out, err = cap.reset() + assert out == "hello\n" + assert not err + + def test_just_err_capture(self): + cap = self.getcapture(out=False, err=True) + print >>sys.stdout, "hello" + print >>sys.stderr, "world" + out, err = cap.reset() + assert err == "world\n" + assert not out + +class TestStdCaptureFD(TestStdCapture): + def getcapture(self, **kw): + return py.io.StdCaptureFD(**kw) def test_intermingling(self): cap = self.getcapture() @@ -114,32 +143,16 @@ assert out == "123" assert err == "abc" -def test_callcapture(): - def func(x, y): - print x - print >>py.std.sys.stderr, y - return 42 - - res, out, err = py.io.StdCaptureFD.call(func, 3, y=4) - assert res == 42 - assert out.startswith("3") - assert err.startswith("4") - -def test_just_out_capture(): - cap = py.io.StdCaptureFD(out=True, err=False) - print >>sys.stdout, "hello" - print >>sys.stderr, "world" - out, err = cap.reset() - assert out == "hello\n" - assert not err - -def test_just_err_capture(): - cap = py.io.StdCaptureFD(out=False, err=True) - print >>sys.stdout, "hello" - print >>sys.stderr, "world" - out, err = cap.reset() - assert err == "world\n" - assert not out + def test_callcapture(self): + def func(x, y): + print x + print >>py.std.sys.stderr, y + return 42 + + res, out, err = py.io.StdCaptureFD.call(func, 3, y=4) + assert res == 42 + assert out.startswith("3") + assert err.startswith("4") def test_capture_no_sys(): cap = py.io.StdCaptureFD(patchsys=False) @@ -151,6 +164,15 @@ assert out == "1" assert err == "2" -#class TestCapturingOnFDs(TestCapturingOnSys): -# def getcapture(self): -# return Capture() +def test_callcapture_nofd(): + def func(x, y): + os.write(1, "hello") + os.write(2, "hello") + print x + print >>py.std.sys.stderr, y + return 42 + + res, out, err = py.io.StdCapture.call(func, 3, y=4) + assert res == 42 + assert out.startswith("3") + assert err.startswith("4") Deleted: /py/trunk/py/io/test/test_simplecapture.py ============================================================================== --- /py/trunk/py/io/test/test_simplecapture.py Thu Feb 1 21:26:27 2007 +++ (empty file) @@ -1,67 +0,0 @@ -import os, sys -import py - -class TestCapturingOnSys: - - def getcapture(self): - return py.io.StdCapture() - - def test_capturing_simple(self): - cap = self.getcapture() - print "hello world" - print >>sys.stderr, "hello error" - out, err = cap.reset() - assert out == "hello world\n" - assert err == "hello error\n" - - def test_capturing_twice_error(self): - cap = self.getcapture() - print "hello" - cap.reset() - py.test.raises(AttributeError, "cap.reset()") - - def test_capturing_modify_sysouterr_in_between(self): - oldout = sys.stdout - olderr = sys.stderr - cap = self.getcapture() - print "hello", - print >>sys.stderr, "world", - sys.stdout = py.std.StringIO.StringIO() - sys.stderr = py.std.StringIO.StringIO() - print "not seen" - print >>sys.stderr, "not seen" - out, err = cap.reset() - assert out == "hello" - assert err == "world" - assert sys.stdout == oldout - assert sys.stderr == olderr - - def test_capturing_error_recursive(self): - cap1 = self.getcapture() - print "cap1" - cap2 = self.getcapture() - print "cap2" - out2, err2 = cap2.reset() - py.test.raises(AttributeError, "cap2.reset()") - out1, err1 = cap1.reset() - assert out1 == "cap1\n" - assert out2 == "cap2\n" - - def test_reading_stdin_while_captured_doesnt_hang(self): - cap = self.getcapture() - try: - py.test.raises(IOError, raw_input) - finally: - cap.reset() - -def test_callcapture_nofd(): - def func(x, y): - print x - print >>py.std.sys.stderr, y - return 42 - - res, out, err = py.io.StdCapture.call(func, 3, y=4) - assert res == 42 - assert out.startswith("3") - assert err.startswith("4") - From guido at codespeak.net Thu Feb 1 21:28:22 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Thu, 1 Feb 2007 21:28:22 +0100 (CET) Subject: [py-svn] r37767 - py/trunk/py/doc Message-ID: <20070201202822.8AB111008D@code0.codespeak.net> Author: guido Date: Thu Feb 1 21:28:21 2007 New Revision: 37767 Modified: py/trunk/py/doc/path.txt Log: Fixed Windows issues in doctests. Modified: py/trunk/py/doc/path.txt ============================================================================== --- py/trunk/py/doc/path.txt (original) +++ py/trunk/py/doc/path.txt Thu Feb 1 21:28:21 2007 @@ -109,9 +109,9 @@ >>> results = [] >>> for fpath in dirpath.visit('*.txt'): ... if 'bar' in fpath.read(): - ... results.append(fpath.relto(dirpath)) + ... results.append(fpath.basename) >>> results - ['textfile1.txt', 'textfile2.txt', 'subdir/textfile2.txt'] + ['textfile1.txt', 'textfile2.txt', 'textfile2.txt'] Joining path types ++++++++++++++++++++ @@ -122,9 +122,10 @@ >>> p1 = py.path.local('/foo/bar') >>> p2 = p1.join('baz/qux') - >>> p2.strpath - '/foo/bar/baz/qux' - >>> p2.relto(p1) + >>> p2 == py.path.local('/foo/bar/baz/qux') + True + >>> sep = py.path.local.sep + >>> p2.relto(p1).replace(sep, '/') # os-specific path sep in the string 'baz/qux' >>> p3 = p1 / 'baz/qux' # the / operator allows joining, too >>> p2 == p3 From hpk at codespeak.net Thu Feb 1 22:16:12 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 1 Feb 2007 22:16:12 +0100 (CET) Subject: [py-svn] r37769 - py/trunk/py/doc Message-ID: <20070201211612.7B0731007F@code0.codespeak.net> Author: hpk Date: Thu Feb 1 22:16:10 2007 New Revision: 37769 Modified: py/trunk/py/doc/misc.txt Log: minor clarifi Modified: py/trunk/py/doc/misc.txt ============================================================================== --- py/trunk/py/doc/misc.txt (original) +++ py/trunk/py/doc/misc.txt Thu Feb 1 22:16:10 2007 @@ -176,8 +176,8 @@ -Python version compatibility helpers -===================================== +Cross-Python Version compatibility helpers +============================================= sources: From hpk at codespeak.net Thu Feb 1 22:18:42 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 1 Feb 2007 22:18:42 +0100 (CET) Subject: [py-svn] r37770 - py/trunk/py/path/svn Message-ID: <20070201211842.CA5F11007F@code0.codespeak.net> Author: hpk Date: Thu Feb 1 22:18:40 2007 New Revision: 37770 Removed: py/trunk/py/path/svn/xsvnbinding.py Log: remove very old try at doing svn bindings Deleted: /py/trunk/py/path/svn/xsvnbinding.py ============================================================================== --- /py/trunk/py/path/svn/xsvnbinding.py Thu Feb 1 22:18:40 2007 +++ (empty file) @@ -1,110 +0,0 @@ -""" - -module defining subversion path objects. - -SvnBindingPath implementation using svn-swig-py-bindings - -""" -# import svn swig-c-bindings - -if 0: - #import svn.client, svn.core, svn.util, atexit - #svn.util.apr_initialize() - #atexit.register(svn.util.apr_terminate) - - #import os, sys, time, re - #from common import * - #from svncommon import _cache, _repocache, SvnPathBase - - class SvnBindingPath(SvnPathBase): - pool = svn.util.svn_pool_create(None) - - def _make_rev_t(self): - rev = svn.core.svn_opt_revision_t() - if self.rev is not None: - rev.kind = svn.core.svn_opt_revision_number - rev.value.number = self.rev - return rev - - def _make_cctx(self): - cctx = svn.client.svn_client_ctx_t() - provider = svn.client.svn_client_get_simple_provider(self.pool) - cctx.auth_baton = svn.util.svn_auth_open([provider], self.pool) - return cctx - - def open(self, mode='r'): - assert 'w' not in mode and 'a' not in mode - assert self.exists() # svn cat returns an empty file otherwise - return os.popen("svn cat -r %s '%s'" % (self.rev, self.strpath)) - # XXX we don't know how to make our own stream - #from svn import client, util, core - #url = self.strpath - #rev = self._make_rev_t() - #cctx = self._make_cctx() - #stream = core.svn_stream_create(None, self.pool) - #client.svn_client_cat(stream, url, rev, cctx, self.pool) - #return stream.get_value() - - def _propget(self, name): - url = self.strpath - rev = self._make_rev_t() - cctx = self._make_cctx() - - table = svn.client.svn_client_propget(name, url, rev, 0, cctx, self.pool) - return str(table.values()[0]) - - def _proplist(self): - url = self.strpath - rev = self._make_rev_t() - cctx = self._make_cctx() - table = svn.client.svn_client_proplist(url, rev, 0, cctx, self.pool) - - if not table: - return {} - content = table[0][1] - for name, value in content.items(): - content[name] = str(value) - return content - - def exists(self): - try: - self.proplist() - except RuntimeError, e: - if e.args[0].lower().find('unknown node')!=-1: - return 0 - raise - return 1 - - def _listdir_nameinfo(self): - """ return a tuple of paths on 'self' as a directory """ - url = self.strpath - rev = self._make_rev_t() - cctx = self._make_cctx() - try: - dic = svn.client.svn_client_ls(url, rev, 0, cctx, self.pool) - except RuntimeError, e: - raise IOError(e.args) - - nameinfo_seq = map(lambda x: (x[0], InfoSvnBinding(x[1])), dic.items()) - return nameinfo_seq - - class InfoSvnBinding: - def __init__(self, _info): - self.size = _info.size - self.time = _info.time - self.last_author = _info.last_author - self.created_rev = _info.created_rev - self.has_props = _info.has_props - - if _info.kind == svn.core.svn_node_dir: - self.kind = 'dir' - elif _info.kind == svn.core.svn_node_file: - self.kind = 'file' - else: - raise ValueError, "unknown kind of svn object" - - self.mtime = self.time / 1000000 - def __eq__(self, other): - return self.__dict__ == other.__dict__ - - SvnPath = SvnBindingPath From hpk at codespeak.net Thu Feb 1 22:52:44 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 1 Feb 2007 22:52:44 +0100 (CET) Subject: [py-svn] r37772 - in py/trunk/py/io: . test Message-ID: <20070201215244.8F28610088@code0.codespeak.net> Author: hpk Date: Thu Feb 1 22:52:42 2007 New Revision: 37772 Modified: py/trunk/py/io/stdcapture.py py/trunk/py/io/test/test_capture.py Log: have both capturings have the same done/reset semantics (should also fix a buildcmodule related problem, e.g. for greenlets) Modified: py/trunk/py/io/stdcapture.py ============================================================================== --- py/trunk/py/io/stdcapture.py (original) +++ py/trunk/py/io/stdcapture.py Thu Feb 1 22:52:42 2007 @@ -22,6 +22,16 @@ return res, out, err call = classmethod(call) + def reset(self): + """ reset sys.stdout and sys.stderr + + returns a tuple of file objects (out, err) for the captured + data + """ + outfile, errfile = self.done() + return outfile.read(), errfile.read() + + class StdCaptureFD(Capture): """ capture Stdout and Stderr both on filedescriptor and sys.stdout/stderr level. @@ -40,18 +50,14 @@ if patchsys: self.err.setasfile('stderr') - def reset(self): - """ reset sys.stdout and sys.stderr - - returns a tuple of file objects (out, err) for the captured - data - """ + def done(self): + """ return (outfile, errfile) and stop capturing. """ outfile = errfile = emptyfile if hasattr(self, 'out'): outfile = self.out.done() if hasattr(self, 'err'): errfile = self.err.done() - return outfile.read(), errfile.read() + return outfile, errfile class StdCapture(Capture): """ capture sys.stdout/sys.stderr (but not system level fd 1 and 2). @@ -76,11 +82,12 @@ sys.stdin = self.newin = DontReadFromInput() def reset(self): - """ return captured output and restore sys.stdout/err.""" + """ return captured output as strings and restore sys.stdout/err.""" x, y = self.done() return x.read(), y.read() def done(self): + """ return (outfile, errfile) and stop capturing. """ o,e = sys.stdout, sys.stderr outfile = errfile = emptyfile if self._out: Modified: py/trunk/py/io/test/test_capture.py ============================================================================== --- py/trunk/py/io/test/test_capture.py (original) +++ py/trunk/py/io/test/test_capture.py Thu Feb 1 22:52:42 2007 @@ -61,7 +61,15 @@ def getcapture(self, **kw): return py.io.StdCapture(**kw) - def test_capturing_simple(self): + def test_capturing_done_simple(self): + cap = self.getcapture() + print "hello world" + print >>sys.stderr, "hello error" + outfile, errfile = cap.done() + assert outfile.read() == "hello world\n" + assert errfile.read() == "hello error\n" + + def test_capturing_reset_simple(self): cap = self.getcapture() print "hello world" print >>sys.stderr, "hello error" From cfbolz at codespeak.net Thu Feb 1 23:27:52 2007 From: cfbolz at codespeak.net (cfbolz at codespeak.net) Date: Thu, 1 Feb 2007 23:27:52 +0100 (CET) Subject: [py-svn] r37773 - py/trunk/py/doc Message-ID: <20070201222752.216231008B@code0.codespeak.net> Author: cfbolz Date: Thu Feb 1 23:27:51 2007 New Revision: 37773 Modified: py/trunk/py/doc/test.txt Log: typo Modified: py/trunk/py/doc/test.txt ============================================================================== --- py/trunk/py/doc/test.txt (original) +++ py/trunk/py/doc/test.txt Thu Feb 1 23:27:51 2007 @@ -654,7 +654,7 @@ * python (2.3 or 2.4 should work) * unix like machine (reliance on ``os.fork``) -Hot to use it +How to use it ----------------------- When you issue ``py.test -d`` then your computer becomes From guido at codespeak.net Thu Feb 1 23:30:53 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Thu, 1 Feb 2007 23:30:53 +0100 (CET) Subject: [py-svn] r37774 - py/trunk/py/doc Message-ID: <20070201223053.652DC1008B@code0.codespeak.net> Author: guido Date: Thu Feb 1 23:30:51 2007 New Revision: 37774 Modified: py/trunk/py/doc/conftest.py py/trunk/py/doc/test_conftest.py Log: Adding support for checking generated API links (for link roles). Modified: py/trunk/py/doc/conftest.py ============================================================================== --- py/trunk/py/doc/conftest.py (original) +++ py/trunk/py/doc/conftest.py Thu Feb 1 23:30:51 2007 @@ -15,7 +15,7 @@ ) _initialized = False -def checkdocutils(): +def checkdocutils(path): global _initialized try: import docutils @@ -23,8 +23,8 @@ py.test.skip("docutils not importable") if not _initialized: from py.__.rest import directive - directive.register_linkrole('api', resolve_linkrole) - directive.register_linkrole('source', resolve_linkrole) + directive.register_linkrole('api', get_resolve_linkrole(path)) + directive.register_linkrole('source', get_resolve_linkrole(path)) _initialized = True def restcheck(path): @@ -32,7 +32,7 @@ if hasattr(path, 'localpath'): localpath = path.localpath _checkskip(localpath) - checkdocutils() + checkdocutils(localpath) import docutils.utils try: @@ -234,24 +234,34 @@ return self.ReSTChecker(p, parent=self) Directory = DocDirectory -def resolve_linkrole(name, text): - if name == 'api': - if text == 'py': - return 'py', '../../apigen/api/index.html' - assert text.startswith('py.'), ( - 'api link "%s" does not point to the py package') % (text,) - dotted_name = text - if dotted_name.find('(') > -1: - dotted_name = dotted_name[:text.find('(')] - dotted_name = '.'.join(dotted_name.split('.')[1:]) # remove pkg root - return text, '../../apigen/api/%s.html' % (dotted_name,) - elif name == 'source': - assert text.startswith('py/'), ('source link "%s" does not point ' - 'to the py package') % (text,) - relpath = '/'.join(text.split('/')[1:]) - if relpath.endswith('/') or not relpath: - relpath += 'index.html' - else: - relpath += '.html' - return text, '../../apigen/source/%s' % (relpath,) - +def get_resolve_linkrole(checkpath=None): + # XXX yuck... + def resolve_linkrole(name, text): + if name == 'api': + if text == 'py': + ret = ('py', '../../apigen/api/index.html') + else: + assert text.startswith('py.'), ( + 'api link "%s" does not point to the py package') % (text,) + dotted_name = text + if dotted_name.find('(') > -1: + dotted_name = dotted_name[:text.find('(')] + # remove pkg root + dotted_name = '.'.join(dotted_name.split('.')[1:]) + ret = (text, '../../apigen/api/%s.html' % (dotted_name,)) + elif name == 'source': + assert text.startswith('py/'), ('source link "%s" does not point ' + 'to the py package') % (text,) + relpath = '/'.join(text.split('/')[1:]) + if relpath.endswith('/') or not relpath: + relpath += 'index.html' + else: + relpath += '.html' + ret = (text, '../../apigen/source/%s' % (relpath,)) + if checkpath: + if not py.path.local(checkpath).join(ret[1]).check(): + raise AssertionError( + '%s linkrole: %s points to non-existant path %s' % ( + name, ret[0], ret[1])) + return ret + return resolve_linkrole Modified: py/trunk/py/doc/test_conftest.py ============================================================================== --- py/trunk/py/doc/test_conftest.py (original) +++ py/trunk/py/doc/test_conftest.py Thu Feb 1 23:30:51 2007 @@ -70,7 +70,8 @@ assert len(l+l2) == 3 def test_resolve_linkrole(): - from py.__.doc.conftest import resolve_linkrole + from py.__.doc.conftest import get_resolve_linkrole + resolve_linkrole = get_resolve_linkrole(None) assert resolve_linkrole('api', 'py.foo.bar') == ( 'py.foo.bar', '../../apigen/api/foo.bar.html') assert resolve_linkrole('api', 'py.foo.bar()') == ( @@ -86,3 +87,14 @@ 'py/', '../../apigen/source/index.html') py.test.raises(AssertionError, 'resolve_linkrole("source", "/foo/bar/")') +def test_resolve_linkrole_relpath(): + from py.__.doc.conftest import get_resolve_linkrole + pypath = tmpdir.join('py') + docpath = pypath.join('doc') + apipath = tmpdir.join('apigen/api') + apipath.ensure('foo.bar.html') + resolve_linkrole = get_resolve_linkrole(docpath) + + assert resolve_linkrole('api', 'py.foo.bar') + py.test.raises(AssertionError, "resolve_linkrole('api', 'py.foo.baz')") + From guido at codespeak.net Fri Feb 2 00:12:53 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Fri, 2 Feb 2007 00:12:53 +0100 (CET) Subject: [py-svn] r37776 - py/trunk/py/doc Message-ID: <20070201231253.827821008A@code0.codespeak.net> Author: guido Date: Fri Feb 2 00:12:49 2007 New Revision: 37776 Modified: py/trunk/py/doc/conftest.py py/trunk/py/doc/misc.txt py/trunk/py/doc/path.txt Log: Fixed some problems with the relative link checker (was using the wrong base path for checks), fixed links in the documents. Modified: py/trunk/py/doc/conftest.py ============================================================================== --- py/trunk/py/doc/conftest.py (original) +++ py/trunk/py/doc/conftest.py Fri Feb 2 00:12:49 2007 @@ -14,25 +14,27 @@ ) ) -_initialized = False -def checkdocutils(path): - global _initialized +def checkdocutils(): try: import docutils except ImportError: py.test.skip("docutils not importable") - if not _initialized: - from py.__.rest import directive - directive.register_linkrole('api', get_resolve_linkrole(path)) - directive.register_linkrole('source', get_resolve_linkrole(path)) - _initialized = True + +def initrestdirectives(path): + from py.__.rest import directive + dirpath = path.dirpath() + # XXX note that this gets called for every test, because the path is + # different every test... + directive.register_linkrole('api', get_resolve_linkrole(dirpath)) + directive.register_linkrole('source', get_resolve_linkrole(dirpath)) def restcheck(path): localpath = path if hasattr(path, 'localpath'): localpath = path.localpath _checkskip(localpath) - checkdocutils(localpath) + checkdocutils() + initrestdirectives(localpath) import docutils.utils try: @@ -262,6 +264,7 @@ if not py.path.local(checkpath).join(ret[1]).check(): raise AssertionError( '%s linkrole: %s points to non-existant path %s' % ( - name, ret[0], ret[1])) + name, ret[0], py.path.local(checkpath).join(ret[1]))) return ret return resolve_linkrole + Modified: py/trunk/py/doc/misc.txt ============================================================================== --- py/trunk/py/doc/misc.txt (original) +++ py/trunk/py/doc/misc.txt Fri Feb 2 00:12:49 2007 @@ -93,7 +93,8 @@ Currently, the py lib offers two ways to interact with system executables. :api:`py.process.cmdexec()` invokes the shell in order to execute a string. The other -one, :api:`py.path.local.sysexec()` lets you directly execute a binary. +one, :api:`py.path.local`'s 'sysexec()' method lets you +directly execute a binary. Both approaches will raise an exception in case of a return- code other than 0 and otherwise return the stdout-output @@ -217,3 +218,4 @@ module) :api:`py.builtin.BaseException` is just ``Exception`` before Python 2.5. + Modified: py/trunk/py/doc/path.txt ============================================================================== --- py/trunk/py/doc/path.txt (original) +++ py/trunk/py/doc/path.txt Fri Feb 2 00:12:49 2007 @@ -209,10 +209,10 @@ of platform-dependencies as much as possible). There is some experimental small approach -(:source:`py/path/gateway`) aiming at having +(:source:`py/path/gateway/`) aiming at having a convenient Remote Path implementation and some considerations about future -works in the according :source:`py/path/gateway/todo.txt` +works in the according :source:`py/path/gateway/TODO.txt` There are various hacks out there to have Memory-Filesystems and even path objects From fijal at codespeak.net Fri Feb 2 00:41:01 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Fri, 2 Feb 2007 00:41:01 +0100 (CET) Subject: [py-svn] r37779 - in py/trunk/py/execnet: . testing Message-ID: <20070201234101.CBE0110075@code0.codespeak.net> Author: fijal Date: Fri Feb 2 00:40:49 2007 New Revision: 37779 Modified: py/trunk/py/execnet/rsync.py py/trunk/py/execnet/rsync_remote.py py/trunk/py/execnet/testing/test_rsync.py Log: Fix and a test for disappearing files. Modified: py/trunk/py/execnet/rsync.py ============================================================================== --- py/trunk/py/execnet/rsync.py (original) +++ py/trunk/py/execnet/rsync.py Fri Feb 2 00:40:49 2007 @@ -85,26 +85,33 @@ elif command == "send": modified_rel_path, checksum = data modifiedpath = os.path.join(self.sourcedir, *modified_rel_path) - f = open(modifiedpath, 'rb') - data = f.read() + try: + f = open(modifiedpath, 'rb') + data = f.read() + except IOError: + data = None # provide info to progress callback function modified_rel_path = "/".join(modified_rel_path) - self.paths[modified_rel_path] = len(data) + if data is not None: + self.paths[modified_rel_path] = len(data) + else: + self.paths[modified_rel_path] = 0 if channel not in self.to_send: self.to_send[channel] = [] self.to_send[channel].append(modified_rel_path) - f.close() - if checksum is not None and checksum == md5.md5(data).digest(): - data = None # not really modified - else: - # ! there is a reason for the interning: - # sharing multiple copies of the file's data - data = intern(data) - print '%s <= %s' % ( - channel.gateway._getremoteaddress(), - modified_rel_path) + if data is not None: + f.close() + if checksum is not None and checksum == md5.md5(data).digest(): + data = None # not really modified + else: + # ! there is a reason for the interning: + # sharing multiple copies of the file's data + data = intern(data) + print '%s <= %s' % ( + channel.gateway._getremoteaddress(), + modified_rel_path) channel.send(data) del data else: @@ -118,7 +125,11 @@ self.links.append(("link", basename, linkpoint)) def _send_directory_structure(self, path): - st = os.lstat(path) + try: + st = os.lstat(path) + except OSError: + self._broadcast((0, 0)) + return if stat.S_ISREG(st.st_mode): # regular file: send a timestamp/size pair self._broadcast((st.st_mtime, st.st_size)) Modified: py/trunk/py/execnet/rsync_remote.py ============================================================================== --- py/trunk/py/execnet/rsync_remote.py (original) +++ py/trunk/py/execnet/rsync_remote.py Fri Feb 2 00:40:49 2007 @@ -65,7 +65,10 @@ f = open(path, 'wb') f.write(data) f.close() - os.utime(path, (time, time)) + try: + os.utime(path, (time, time)) + except OSError: + pass del data channel.send(("links", None)) Modified: py/trunk/py/execnet/testing/test_rsync.py ============================================================================== --- py/trunk/py/execnet/testing/test_rsync.py (original) +++ py/trunk/py/execnet/testing/test_rsync.py Fri Feb 2 00:40:49 2007 @@ -78,3 +78,24 @@ assert total == {("list", 110):True, ("ack", 100):True, ("ack", 10):True} +def test_file_disappearing(): + base = py.test.ensuretemp("file_disappearing") + dest = base.join("dest") + source = base.join("source") + source.ensure("ex").write("a" * 100) + source.ensure("ex2").write("a" * 100) + + class DRsync(RSync): + def filter(self, x): + if x.endswith("ex2"): + self.x = 1 + source.join("ex2").remove() + return True + + rsync = DRsync() + rsync.add_target(gw, dest) + rsync.send(source) + assert rsync.x == 1 + assert len(dest.listdir()) == 1 + assert len(source.listdir()) == 1 + From hpk at codespeak.net Fri Feb 2 00:58:10 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 2 Feb 2007 00:58:10 +0100 (CET) Subject: [py-svn] r37780 - in py/trunk/py/execnet: . testing Message-ID: <20070201235810.3D16410078@code0.codespeak.net> Author: hpk Date: Fri Feb 2 00:57:55 2007 New Revision: 37780 Modified: py/trunk/py/execnet/gateway.py py/trunk/py/execnet/register.py py/trunk/py/execnet/rsync.py py/trunk/py/execnet/testing/test_gateway.py Log: simplifying code a bit (but test_confusion* for Ssh still fails) Modified: py/trunk/py/execnet/gateway.py ============================================================================== --- py/trunk/py/execnet/gateway.py (original) +++ py/trunk/py/execnet/gateway.py Fri Feb 2 00:57:55 2007 @@ -31,12 +31,12 @@ sysex = (KeyboardInterrupt, SystemExit) class Gateway(object): - num_worker_threads = 2 ThreadOut = ThreadOut + remoteaddress = "" def __init__(self, io, startcount=2, maxthreads=None): global registered_cleanup - self._execpool = WorkerPool() + self._execpool = WorkerPool(maxthreads=maxthreads) ## self.running = True self.io = io self._outgoing = Queue.Queue() @@ -50,7 +50,7 @@ sender = self.thread_sender) def __repr__(self): - addr = self._getremoteaddress() + addr = self.remoteaddress if addr: addr = '[%s]' % (addr,) else: @@ -67,9 +67,6 @@ return "<%s%s %s/%s (%s active channels)>" %( self.__class__.__name__, addr, r, s, i) - def _getremoteaddress(self): - return None - ## def _local_trystopexec(self): ## self._execpool.shutdown() Modified: py/trunk/py/execnet/register.py ============================================================================== --- py/trunk/py/execnet/register.py (original) +++ py/trunk/py/execnet/register.py Fri Feb 2 00:57:55 2007 @@ -136,9 +136,7 @@ sock.connect((host, port)) io = inputoutput.SocketIO(sock) super(SocketGateway, self).__init__(io=io) - - def _getremoteaddress(self): - return '%s:%d' % (self.host, self.port) + self.remoteaddress = '%s:%d' % (self.host, self.port) def remote_install(cls, gateway, hostport=None): """ return a connected socket gateway through the @@ -167,7 +165,7 @@ class SshGateway(PopenCmdGateway): def __init__(self, sshaddress, remotepython='python', identity=None): - self.sshaddress = sshaddress + self.remoteaddress = sshaddress remotecmd = '%s -u -c "exec input()"' % (remotepython,) cmdline = [sshaddress, remotecmd] # XXX Unix style quoting @@ -179,9 +177,6 @@ cmdline.insert(0, cmd) super(SshGateway, self).__init__(' '.join(cmdline)) - def _getremoteaddress(self): - return self.sshaddress - class ExecGateway(PopenGateway): def remote_exec_sync_stdcapture(self, lines, callback): # hack: turn the content of the cell into Modified: py/trunk/py/execnet/rsync.py ============================================================================== --- py/trunk/py/execnet/rsync.py (original) +++ py/trunk/py/execnet/rsync.py Fri Feb 2 00:57:55 2007 @@ -110,7 +110,7 @@ # sharing multiple copies of the file's data data = intern(data) print '%s <= %s' % ( - channel.gateway._getremoteaddress(), + channel.gateway.remoteaddress, modified_rel_path) channel.send(data) del data Modified: py/trunk/py/execnet/testing/test_gateway.py ============================================================================== --- py/trunk/py/execnet/testing/test_gateway.py (original) +++ py/trunk/py/execnet/testing/test_gateway.py Fri Feb 2 00:57:55 2007 @@ -62,8 +62,8 @@ def setup_class(cls): cls.gw = py.execnet.PopenGateway() -## def teardown_class(cls): -## cls.gw.exit() + def teardown_class(cls): + cls.gw.exit() class BasicRemoteExecution: def test_correct_setup(self): @@ -455,7 +455,7 @@ cls.gw = py.execnet.SshGateway(option.sshtarget) def test_sshaddress(self): - assert self.gw.sshaddress == option.sshtarget + assert self.gw.remoteaddress == option.sshtarget def test_failed_connexion(self): gw = py.execnet.SshGateway('nowhere.codespeak.net') From guido at codespeak.net Fri Feb 2 00:59:02 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Fri, 2 Feb 2007 00:59:02 +0100 (CET) Subject: [py-svn] r37781 - py/trunk/py/apigen Message-ID: <20070201235902.81B5E1007E@code0.codespeak.net> Author: guido Date: Fri Feb 2 00:58:57 2007 New Revision: 37781 Modified: py/trunk/py/apigen/apigen.py Log: Added 'execnet.channel.Channel' object to the to-be-documented items. Modified: py/trunk/py/apigen/apigen.py ============================================================================== --- py/trunk/py/apigen/apigen.py (original) +++ py/trunk/py/apigen/apigen.py Fri Feb 2 00:58:57 2007 @@ -15,7 +15,9 @@ def get_documentable_items(pkgdir): sys.path.insert(0, str(pkgdir.dirpath())) rootmod = __import__(pkgdir.basename) - return 'py', pkg_to_dict(rootmod) + d = pkg_to_dict(rootmod) + d['execnet.Channel'] = py.__.execnet.channel.Channel + return 'py', d def build(pkgdir, dsa, capture): l = linker.Linker() From guido at codespeak.net Fri Feb 2 01:01:06 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Fri, 2 Feb 2007 01:01:06 +0100 (CET) Subject: [py-svn] r37782 - py/trunk/py/doc Message-ID: <20070202000106.6B5DB10077@code0.codespeak.net> Author: guido Date: Fri Feb 2 01:00:50 2007 New Revision: 37782 Modified: py/trunk/py/doc/conftest.py py/trunk/py/doc/test_conftest.py Log: Changed the checking in resolve_linkrole() so that instead of the apigen results (the directory with HTML files) it uses the py lib object tree and source tree to find out whether links are valid. Modified: py/trunk/py/doc/conftest.py ============================================================================== --- py/trunk/py/doc/conftest.py (original) +++ py/trunk/py/doc/conftest.py Fri Feb 2 01:00:50 2007 @@ -14,19 +14,18 @@ ) ) +_initialized = False def checkdocutils(): + global _initialized try: import docutils except ImportError: py.test.skip("docutils not importable") - -def initrestdirectives(path): - from py.__.rest import directive - dirpath = path.dirpath() - # XXX note that this gets called for every test, because the path is - # different every test... - directive.register_linkrole('api', get_resolve_linkrole(dirpath)) - directive.register_linkrole('source', get_resolve_linkrole(dirpath)) + if not _initialized: + from py.__.rest import directive + directive.register_linkrole('api', resolve_linkrole) + directive.register_linkrole('source', resolve_linkrole) + _initialized = True def restcheck(path): localpath = path @@ -34,7 +33,6 @@ localpath = path.localpath _checkskip(localpath) checkdocutils() - initrestdirectives(localpath) import docutils.utils try: @@ -236,35 +234,42 @@ return self.ReSTChecker(p, parent=self) Directory = DocDirectory -def get_resolve_linkrole(checkpath=None): - # XXX yuck... - def resolve_linkrole(name, text): - if name == 'api': - if text == 'py': - ret = ('py', '../../apigen/api/index.html') - else: - assert text.startswith('py.'), ( - 'api link "%s" does not point to the py package') % (text,) - dotted_name = text - if dotted_name.find('(') > -1: - dotted_name = dotted_name[:text.find('(')] - # remove pkg root - dotted_name = '.'.join(dotted_name.split('.')[1:]) - ret = (text, '../../apigen/api/%s.html' % (dotted_name,)) - elif name == 'source': - assert text.startswith('py/'), ('source link "%s" does not point ' - 'to the py package') % (text,) - relpath = '/'.join(text.split('/')[1:]) - if relpath.endswith('/') or not relpath: - relpath += 'index.html' - else: - relpath += '.html' - ret = (text, '../../apigen/source/%s' % (relpath,)) - if checkpath: - if not py.path.local(checkpath).join(ret[1]).check(): - raise AssertionError( - '%s linkrole: %s points to non-existant path %s' % ( - name, ret[0], py.path.local(checkpath).join(ret[1]))) - return ret - return resolve_linkrole +def resolve_linkrole(name, text, check=True): + if name == 'api': + if text == 'py': + return ('py', '../../apigen/api/index.html') + else: + assert text.startswith('py.'), ( + 'api link "%s" does not point to the py package') % (text,) + dotted_name = text + if dotted_name.find('(') > -1: + dotted_name = dotted_name[:text.find('(')] + # remove pkg root + path = dotted_name.split('.')[1:] + dotted_name = '.'.join(path) + obj = py + if check: + for chunk in path: + try: + obj = getattr(obj, chunk) + except AttributeError: + raise AssertionError( + 'problem with linkrole :api:`%s`: can not resolve ' + 'dotted name %s' % (text, dotted_name,)) + return (text, '../../apigen/api/%s.html' % (dotted_name,)) + elif name == 'source': + assert text.startswith('py/'), ('source link "%s" does not point ' + 'to the py package') % (text,) + relpath = '/'.join(text.split('/')[1:]) + if check: + pkgroot = py.__package__.getpath() + abspath = pkgroot.join(relpath) + assert pkgroot.join(relpath).check(), ( + 'problem with linkrole :source:`%s`: ' + 'path %s does not exist' % (text, relpath)) + if relpath.endswith('/') or not relpath: + relpath += 'index.html' + else: + relpath += '.html' + return (text, '../../apigen/source/%s' % (relpath,)) Modified: py/trunk/py/doc/test_conftest.py ============================================================================== --- py/trunk/py/doc/test_conftest.py (original) +++ py/trunk/py/doc/test_conftest.py Fri Feb 2 01:00:50 2007 @@ -70,31 +70,30 @@ assert len(l+l2) == 3 def test_resolve_linkrole(): - from py.__.doc.conftest import get_resolve_linkrole - resolve_linkrole = get_resolve_linkrole(None) - assert resolve_linkrole('api', 'py.foo.bar') == ( + from py.__.doc.conftest import resolve_linkrole + assert resolve_linkrole('api', 'py.foo.bar', False) == ( 'py.foo.bar', '../../apigen/api/foo.bar.html') - assert resolve_linkrole('api', 'py.foo.bar()') == ( + assert resolve_linkrole('api', 'py.foo.bar()', False) == ( 'py.foo.bar()', '../../apigen/api/foo.bar.html') - assert resolve_linkrole('api', 'py') == ( + assert resolve_linkrole('api', 'py', False) == ( 'py', '../../apigen/api/index.html') py.test.raises(AssertionError, 'resolve_linkrole("api", "foo.bar")') - assert resolve_linkrole('source', 'py/foo/bar.py') == ( + assert resolve_linkrole('source', 'py/foo/bar.py', False) == ( 'py/foo/bar.py', '../../apigen/source/foo/bar.py.html') - assert resolve_linkrole('source', 'py/foo/') == ( + assert resolve_linkrole('source', 'py/foo/', False) == ( 'py/foo/', '../../apigen/source/foo/index.html') - assert resolve_linkrole('source', 'py/') == ( + assert resolve_linkrole('source', 'py/', False) == ( 'py/', '../../apigen/source/index.html') py.test.raises(AssertionError, 'resolve_linkrole("source", "/foo/bar/")') -def test_resolve_linkrole_relpath(): - from py.__.doc.conftest import get_resolve_linkrole - pypath = tmpdir.join('py') - docpath = pypath.join('doc') - apipath = tmpdir.join('apigen/api') - apipath.ensure('foo.bar.html') - resolve_linkrole = get_resolve_linkrole(docpath) - - assert resolve_linkrole('api', 'py.foo.bar') +def test_resolve_linkrole_check_api(): + from py.__.doc.conftest import resolve_linkrole + assert resolve_linkrole('api', 'py.test.ensuretemp') py.test.raises(AssertionError, "resolve_linkrole('api', 'py.foo.baz')") +def test_resolve_linkrole_check_source(): + from py.__.doc.conftest import resolve_linkrole + assert resolve_linkrole('source', 'py/path/common.py') + py.test.raises(AssertionError, + "resolve_linkrole('source', 'py/foo/bar.py')") + From hpk at codespeak.net Fri Feb 2 01:32:27 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 2 Feb 2007 01:32:27 +0100 (CET) Subject: [py-svn] r37783 - py/trunk/py/execnet/testing Message-ID: <20070202003227.9CD3910077@code0.codespeak.net> Author: hpk Date: Fri Feb 2 01:32:24 2007 New Revision: 37783 Modified: py/trunk/py/execnet/testing/test_gateway.py Log: puh, it took me quite a while to find out why ssh-stdout-confusion tests involving "os.write(1, ...)" were passing on earlier revisions: in 35888 a test name got fixed, but when you specify "-S somehost" then this will hang, so i am skipping the test for now, this never worked. Modified: py/trunk/py/execnet/testing/test_gateway.py ============================================================================== --- py/trunk/py/execnet/testing/test_gateway.py (original) +++ py/trunk/py/execnet/testing/test_gateway.py Fri Feb 2 01:32:24 2007 @@ -454,6 +454,9 @@ py.test.skip("no known ssh target, use -S to set one") cls.gw = py.execnet.SshGateway(option.sshtarget) + def test_confusion_from_os_write_stdout(self): + py.test.skip("writing to FD 1 with SshGateways not supported yet") + def test_sshaddress(self): assert self.gw.remoteaddress == option.sshtarget From hpk at codespeak.net Fri Feb 2 01:34:41 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 2 Feb 2007 01:34:41 +0100 (CET) Subject: [py-svn] r37784 - py/trunk/py/execnet Message-ID: <20070202003441.D89641007E@code0.codespeak.net> Author: hpk Date: Fri Feb 2 01:34:40 2007 New Revision: 37784 Modified: py/trunk/py/execnet/gateway.py Log: privatizing some attributes Modified: py/trunk/py/execnet/gateway.py ============================================================================== --- py/trunk/py/execnet/gateway.py (original) +++ py/trunk/py/execnet/gateway.py Fri Feb 2 01:34:40 2007 @@ -31,7 +31,7 @@ sysex = (KeyboardInterrupt, SystemExit) class Gateway(object): - ThreadOut = ThreadOut + _ThreadOut = ThreadOut remoteaddress = "" def __init__(self, io, startcount=2, maxthreads=None): @@ -46,8 +46,8 @@ atexit.register(cleanup_atexit) registered_cleanup = True _active_sendqueues[self._outgoing] = True - self.pool = NamedThreadPool(receiver = self.thread_receiver, - sender = self.thread_sender) + self.pool = NamedThreadPool(receiver = self._thread_receiver, + sender = self._thread_sender) def __repr__(self): addr = self.remoteaddress @@ -92,7 +92,7 @@ excinfo[1]) self._trace(errortext) - def thread_receiver(self): + def _thread_receiver(self): """ thread to read and handle Messages half-sync-half-async. """ try: from sys import exc_info @@ -113,7 +113,7 @@ self.channelfactory._finished_receiving() self._trace('leaving %r' % threading.currentThread()) - def thread_sender(self): + def _thread_sender(self): """ thread to send Messages over the wire. """ try: from sys import exc_info @@ -141,7 +141,7 @@ for name, id in ('stdout', outid), ('stderr', errid): if id: channel = self.channelfactory.new(outid) - out = ThreadOut(sys, name) + out = self._ThreadOut(sys, name) out.setwritefunc(channel.send) l.append((out, channel)) def close(): @@ -150,7 +150,7 @@ channel.close() return close - def thread_executor(self, channel, (source, outid, errid)): + def _thread_executor(self, channel, (source, outid, errid)): """ worker thread to execute source objects from the execution queue. """ from sys import exc_info try: @@ -177,7 +177,7 @@ def _local_schedulexec(self, channel, sourcetask): self._trace("dispatching exec") - self._execpool.dispatch(self.thread_executor, channel, sourcetask) + self._execpool.dispatch(self._thread_executor, channel, sourcetask) def _newredirectchannelid(self, callback): if callback is None: @@ -231,7 +231,7 @@ channel = self.remote_exec(""" import sys outchannel = channel.receive() - outchannel.gateway.ThreadOut(sys, %r).setdefaultwriter(outchannel.send) + outchannel.gateway._ThreadOut(sys, %r).setdefaultwriter(outchannel.send) """ % name) channel.send(outchannel) clist.append(channel) @@ -243,7 +243,7 @@ if out: c = self.remote_exec(""" import sys - channel.gateway.ThreadOut(sys, %r).resetdefault() + channel.gateway._ThreadOut(sys, %r).resetdefault() """ % name) c.waitclose(1.0) return Handle() From hpk at codespeak.net Fri Feb 2 02:02:57 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 2 Feb 2007 02:02:57 +0100 (CET) Subject: [py-svn] r37785 - in py/trunk/py/execnet: . testing Message-ID: <20070202010257.BF85110080@code0.codespeak.net> Author: hpk Date: Fri Feb 2 02:02:55 2007 New Revision: 37785 Modified: py/trunk/py/execnet/channel.py py/trunk/py/execnet/gateway.py py/trunk/py/execnet/message.py py/trunk/py/execnet/register.py py/trunk/py/execnet/testing/test_gateway.py Log: privatizing some more attributes Modified: py/trunk/py/execnet/channel.py ============================================================================== --- py/trunk/py/execnet/channel.py (original) +++ py/trunk/py/execnet/channel.py Fri Feb 2 02:02:55 2007 @@ -37,10 +37,10 @@ def setcallback(self, callback, endmarker=NO_ENDMARKER_WANTED): queue = self._items - lock = self.gateway.channelfactory._receivelock + lock = self.gateway._channelfactory._receivelock lock.acquire() try: - _callbacks = self.gateway.channelfactory._callbacks + _callbacks = self.gateway._channelfactory._callbacks dictvalue = (callback, endmarker) if _callbacks.setdefault(self.id, dictvalue) != dictvalue: raise IOError("%r has callback already registered" %(self,)) @@ -58,7 +58,7 @@ callback(olditem) if self._closed or self._receiveclosed.isSet(): # no need to keep a callback - self.gateway.channelfactory._close_callback(self.id) + self.gateway._channelfactory._close_callback(self.id) finally: lock.release() @@ -129,7 +129,7 @@ queue = self._items if queue is not None: queue.put(ENDMARKER) - self.gateway.channelfactory._no_longer_opened(self.id) + self.gateway._channelfactory._no_longer_opened(self.id) def waitclose(self, timeout=None): """ wait until this channel is closed (or the remote side Modified: py/trunk/py/execnet/gateway.py ============================================================================== --- py/trunk/py/execnet/gateway.py (original) +++ py/trunk/py/execnet/gateway.py Fri Feb 2 02:02:55 2007 @@ -10,7 +10,7 @@ # other modules) execute not only on the local side but # also on any gateway's remote side. On such remote sides # we cannot assume the py library to be there and -# InstallableGateway.remote_bootstrap_gateway() (located +# InstallableGateway._remote_bootstrap_gateway() (located # in register.py) will take care to send source fragments # to the other side. Yes, it is fragile but we have a # few tests that try to catch when we mess up. @@ -38,15 +38,15 @@ global registered_cleanup self._execpool = WorkerPool(maxthreads=maxthreads) ## self.running = True - self.io = io + self._io = io self._outgoing = Queue.Queue() - self.channelfactory = ChannelFactory(self, startcount) + self._channelfactory = ChannelFactory(self, startcount) ## self._exitlock = threading.Lock() if not registered_cleanup: atexit.register(cleanup_atexit) registered_cleanup = True _active_sendqueues[self._outgoing] = True - self.pool = NamedThreadPool(receiver = self._thread_receiver, + self._pool = NamedThreadPool(receiver = self._thread_receiver, sender = self._thread_sender) def __repr__(self): @@ -56,11 +56,11 @@ else: addr = '' try: - r = (len(self.pool.getstarted('receiver')) + r = (len(self._pool.getstarted('receiver')) and "receiving" or "not receiving") - s = (len(self.pool.getstarted('sender')) + s = (len(self._pool.getstarted('sender')) and "sending" or "not sending") - i = len(self.channelfactory.channels()) + i = len(self._channelfactory.channels()) except AttributeError: r = s = "uninitialized" i = "no" @@ -98,7 +98,7 @@ from sys import exc_info while 1: try: - msg = Message.readfrom(self.io) + msg = Message.readfrom(self._io) self._trace("received <- %r" % msg) msg.received(self) except sysex: @@ -110,7 +110,7 @@ break finally: self._outgoing.put(None) - self.channelfactory._finished_receiving() + self._channelfactory._finished_receiving() self._trace('leaving %r' % threading.currentThread()) def _thread_sender(self): @@ -121,9 +121,9 @@ msg = self._outgoing.get() try: if msg is None: - self.io.close_write() + self._io.close_write() break - msg.writeto(self.io) + msg.writeto(self._io) except: excinfo = exc_info() self._traceex(excinfo) @@ -140,7 +140,7 @@ l = [] for name, id in ('stdout', outid), ('stderr', errid): if id: - channel = self.channelfactory.new(outid) + channel = self._channelfactory.new(outid) out = self._ThreadOut(sys, name) out.setwritefunc(channel.send) l.append((out, channel)) @@ -196,7 +196,7 @@ # def newchannel(self): """ return new channel object. """ - return self.channelfactory.new() + return self._channelfactory.new() def remote_exec(self, source, stdout=None, stderr=None): """ return channel object for communicating with the asynchronously @@ -218,7 +218,7 @@ (source, outid, errid))) return channel - def remote_redirect(self, stdout=None, stderr=None): + def _remote_redirect(self, stdout=None, stderr=None): """ return a handle representing a redirection of a remote end's stdout to a local file object. with handle.close() the redirection will be reverted. @@ -262,7 +262,7 @@ ## try: ## if self.running: ## self.running = False -## if not self.pool.getstarted('sender'): +## if not self._pool.getstarted('sender'): ## raise IOError("sender thread not alive anymore!") ## self._outgoing.put(None) ## self._trace("exit procedure triggered, pid %d " % (os.getpid(),)) @@ -279,7 +279,7 @@ def join(self, joinexec=True): current = threading.currentThread() - for x in self.pool.getstarted(): + for x in self._pool.getstarted(): if x != current: self._trace("joining %s" % x) x.join() Modified: py/trunk/py/execnet/message.py ============================================================================== --- py/trunk/py/execnet/message.py (original) +++ py/trunk/py/execnet/message.py Fri Feb 2 02:02:55 2007 @@ -62,32 +62,32 @@ class CHANNEL_OPEN(Message): def received(self, gateway): - channel = gateway.channelfactory.new(self.channelid) + channel = gateway._channelfactory.new(self.channelid) gateway._local_schedulexec(channel=channel, sourcetask=self.data) class CHANNEL_NEW(Message): def received(self, gateway): """ receive a remotely created new (sub)channel. """ newid = self.data - newchannel = gateway.channelfactory.new(newid) - gateway.channelfactory._local_receive(self.channelid, newchannel) + newchannel = gateway._channelfactory.new(newid) + gateway._channelfactory._local_receive(self.channelid, newchannel) class CHANNEL_DATA(Message): def received(self, gateway): - gateway.channelfactory._local_receive(self.channelid, self.data) + gateway._channelfactory._local_receive(self.channelid, self.data) class CHANNEL_CLOSE(Message): def received(self, gateway): - gateway.channelfactory._local_close(self.channelid) + gateway._channelfactory._local_close(self.channelid) class CHANNEL_CLOSE_ERROR(Message): def received(self, gateway): - remote_error = gateway.channelfactory.RemoteError(self.data) - gateway.channelfactory._local_close(self.channelid, remote_error) + remote_error = gateway._channelfactory.RemoteError(self.data) + gateway._channelfactory._local_close(self.channelid, remote_error) class CHANNEL_LAST_MESSAGE(Message): def received(self, gateway): - gateway.channelfactory._local_last_message(self.channelid) + gateway._channelfactory._local_last_message(self.channelid) classes = [x for x in locals().values() if hasattr(x, '__bases__')] classes.sort(lambda x,y : cmp(x.__name__, y.__name__)) Modified: py/trunk/py/execnet/register.py ============================================================================== --- py/trunk/py/execnet/register.py (original) +++ py/trunk/py/execnet/register.py Fri Feb 2 02:02:55 2007 @@ -29,10 +29,10 @@ class InstallableGateway(gateway.Gateway): """ initialize gateways on both sides of a inputoutput object. """ def __init__(self, io): - self.remote_bootstrap_gateway(io) + self._remote_bootstrap_gateway(io) super(InstallableGateway, self).__init__(io=io, startcount=1) - def remote_bootstrap_gateway(self, io, extra=''): + def _remote_bootstrap_gateway(self, io, extra=''): """ return Gateway with a asynchronously remotely initialized counterpart Gateway (which may or may not succeed). Note that the other sides gateways starts enumerating @@ -84,7 +84,7 @@ cmd = '%s -u -c "exec input()"' % python super(PopenGateway, self).__init__(cmd) - def remote_bootstrap_gateway(self, io, extra=''): + def _remote_bootstrap_gateway(self, io, extra=''): # XXX the following hack helps us to import the same version # of the py lib and other dependcies, but only works for # PopenGateways because we can assume to have access to @@ -126,7 +126,7 @@ """)), "" ]) - super(PopenGateway, self).remote_bootstrap_gateway(io, s) + super(PopenGateway, self)._remote_bootstrap_gateway(io, s) class SocketGateway(InstallableGateway): def __init__(self, host, port): Modified: py/trunk/py/execnet/testing/test_gateway.py ============================================================================== --- py/trunk/py/execnet/testing/test_gateway.py (original) +++ py/trunk/py/execnet/testing/test_gateway.py Fri Feb 2 02:02:55 2007 @@ -62,13 +62,13 @@ def setup_class(cls): cls.gw = py.execnet.PopenGateway() - def teardown_class(cls): - cls.gw.exit() + #def teardown_class(cls): + # cls.gw.exit() class BasicRemoteExecution: def test_correct_setup(self): for x in 'sender', 'receiver': - assert self.gw.pool.getstarted(x) + assert self.gw._pool.getstarted(x) def test_repr_doesnt_crash(self): assert isinstance(repr(self), str) @@ -127,13 +127,13 @@ py.test.raises(channel.RemoteError, channel.receive) def test_channel__local_close(self): - channel = self.gw.channelfactory.new() - self.gw.channelfactory._local_close(channel.id) + channel = self.gw._channelfactory.new() + self.gw._channelfactory._local_close(channel.id) channel.waitclose(0.1) def test_channel__local_close_error(self): - channel = self.gw.channelfactory.new() - self.gw.channelfactory._local_close(channel.id, + channel = self.gw._channelfactory.new() + self.gw._channelfactory._local_close(channel.id, channel.RemoteError("error")) py.test.raises(channel.RemoteError, channel.waitclose, 0.01) @@ -177,10 +177,10 @@ # check that the both sides previous channels are really gone channel.waitclose(0.3) - assert channel.id not in self.gw.channelfactory._channels - #assert c.id not in self.gw.channelfactory + assert channel.id not in self.gw._channelfactory._channels + #assert c.id not in self.gw._channelfactory newchan = self.gw.remote_exec(''' - assert %d not in channel.gateway.channelfactory._channels + assert %d not in channel.gateway._channelfactory._channels ''' % (channel.id)) newchan.waitclose(0.3) @@ -278,7 +278,7 @@ def test_remote_redirect_stdout(self): out = py.std.StringIO.StringIO() - handle = self.gw.remote_redirect(stdout=out) + handle = self.gw._remote_redirect(stdout=out) c = self.gw.remote_exec("print 42") c.waitclose(1.0) handle.close() From hpk at codespeak.net Fri Feb 2 02:05:33 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 2 Feb 2007 02:05:33 +0100 (CET) Subject: [py-svn] r37786 - py/trunk/py/execnet Message-ID: <20070202010533.CE24610080@code0.codespeak.net> Author: hpk Date: Fri Feb 2 02:05:31 2007 New Revision: 37786 Modified: py/trunk/py/execnet/gateway.py Log: improve docstring Modified: py/trunk/py/execnet/gateway.py ============================================================================== --- py/trunk/py/execnet/gateway.py (original) +++ py/trunk/py/execnet/gateway.py Fri Feb 2 02:05:31 2007 @@ -202,6 +202,10 @@ """ return channel object for communicating with the asynchronously executing 'source' code which will have a corresponding 'channel' object in its executing namespace. + + You may provide callback functions 'stdout' and 'stderr' + which will get called with the remote stdout/stderr output + piece by piece. """ try: source = str(Source(source)) From hpk at codespeak.net Fri Feb 2 02:07:39 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 2 Feb 2007 02:07:39 +0100 (CET) Subject: [py-svn] r37787 - py/trunk/py/execnet Message-ID: <20070202010739.5190010088@code0.codespeak.net> Author: hpk Date: Fri Feb 2 02:07:37 2007 New Revision: 37787 Modified: py/trunk/py/execnet/gateway.py Log: more precision at shutdown: first remove from the sendqueue, then trigger the queue to shutdown. (This is an attempt to get rid of the still appearing "exception in thread" messages) Modified: py/trunk/py/execnet/gateway.py ============================================================================== --- py/trunk/py/execnet/gateway.py (original) +++ py/trunk/py/execnet/gateway.py Fri Feb 2 02:07:37 2007 @@ -275,11 +275,12 @@ ## self._exitlock.release() def exit(self): - self._outgoing.put(None) try: del _active_sendqueues[self._outgoing] except KeyError: pass + else: + self._outgoing.put(None) def join(self, joinexec=True): current = threading.currentThread() From hpk at codespeak.net Fri Feb 2 02:18:10 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 2 Feb 2007 02:18:10 +0100 (CET) Subject: [py-svn] r37788 - py/trunk/py/apigen Message-ID: <20070202011810.15D9010088@code0.codespeak.net> Author: hpk Date: Fri Feb 2 02:18:06 2007 New Revision: 37788 Modified: py/trunk/py/apigen/apigen.py Log: guido, unless i am missing something there seem to be two problems with your making Channel's public: you didn't import it (py.__.* is not lazy) and it fails the apigen tests even after fixing it. Modified: py/trunk/py/apigen/apigen.py ============================================================================== --- py/trunk/py/apigen/apigen.py (original) +++ py/trunk/py/apigen/apigen.py Fri Feb 2 02:18:06 2007 @@ -16,7 +16,8 @@ sys.path.insert(0, str(pkgdir.dirpath())) rootmod = __import__(pkgdir.basename) d = pkg_to_dict(rootmod) - d['execnet.Channel'] = py.__.execnet.channel.Channel + from py.__.execnet.channel import Channel + #d['execnet.Channel'] = Channel # XXX doesn't work return 'py', d def build(pkgdir, dsa, capture): From hpk at codespeak.net Fri Feb 2 02:22:46 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 2 Feb 2007 02:22:46 +0100 (CET) Subject: [py-svn] r37789 - py/trunk/py/doc Message-ID: <20070202012246.8B7EE10088@code0.codespeak.net> Author: hpk Date: Fri Feb 2 02:22:43 2007 New Revision: 37789 Modified: py/trunk/py/doc/TODO.txt py/trunk/py/doc/log.txt Log: small updates and streamlining of docs Modified: py/trunk/py/doc/TODO.txt ============================================================================== --- py/trunk/py/doc/TODO.txt (original) +++ py/trunk/py/doc/TODO.txt Fri Feb 2 02:22:43 2007 @@ -22,7 +22,8 @@ streamline exported API ------------------------------------- -* (hpk, should be done) move not-to-be-exported Gateway() methods to _ - methods. +* (DONE) move not-to-be-exported Gateway() methods to _ - methods. + * docstrings for all exported API * (DONE) remove: test.compat.TestCAse Modified: py/trunk/py/doc/log.txt ============================================================================== --- py/trunk/py/doc/log.txt (original) +++ py/trunk/py/doc/log.txt Fri Feb 2 02:22:43 2007 @@ -15,8 +15,8 @@ :code:`py.log` module. It was written by Francois Pinard and also contains some ideas for enhancing the py.log facilities. - -NOTE that `py.log` is an ALPHA interface, it can change any time. +NOTE that `py.log` is subject to refactorings, it may change with +the next release. This document is meant to trigger or facilitate discussions. It shamelessly steals from the `Agile Testing`__ comments, and from other sources as well, @@ -24,8 +24,6 @@ __ http://agiletesting.blogspot.com/2005/06/keyword-based-logging-with-py-library.html -My knowledge of English is fairly rough and approximative, I hope -readers will kindly and spontaneously offer stylistic corrections. Logging organisation ==================== From hpk at codespeak.net Fri Feb 2 02:44:33 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 2 Feb 2007 02:44:33 +0100 (CET) Subject: [py-svn] r37790 - in py/trunk/py/execnet: . testing Message-ID: <20070202014433.BE39410071@code0.codespeak.net> Author: hpk Date: Fri Feb 2 02:44:31 2007 New Revision: 37790 Modified: py/trunk/py/execnet/register.py py/trunk/py/execnet/testing/test_gateway.py Log: fix Ssh remote sanitzing of FD 1 and 0 and factor out the code in a function Modified: py/trunk/py/execnet/register.py ============================================================================== --- py/trunk/py/execnet/register.py (original) +++ py/trunk/py/execnet/register.py Fri Feb 2 02:44:31 2007 @@ -97,33 +97,8 @@ s = "\n".join([extra, "import sys ; sys.path[:0] = %r" % (plist,), "import os ; os.environ['PYTHONPATH'] = %r" % ppath, - # redirect file descriptors 0 and 1 to /dev/null, to avoid - # complete confusion (this is independent from the sys.stdout - # and sys.stderr redirection that gateway.remote_exec() can do) - # note that we redirect fd 2 on win too, since for some reason that - # blocks there, while it works (sending to stderr if possible else - # ignoring) on *nix - str(py.code.Source(""" - try: - devnull = os.devnull - except AttributeError: - if os.name == 'nt': - devnull = 'NUL' - else: - devnull = '/dev/null' - sys.stdin = os.fdopen(os.dup(0), 'rb', 0) - sys.stdout = os.fdopen(os.dup(1), 'wb', 0) - if os.name == 'nt': - sys.stderr = os.fdopen(os.dup(2), 'wb', 0) - fd = os.open(devnull, os.O_RDONLY) - os.dup2(fd, 0) - os.close(fd) - fd = os.open(devnull, os.O_WRONLY) - os.dup2(fd, 1) - if os.name == 'nt': - os.dup2(fd, 2) - os.close(fd) - """)), + str(py.code.Source(stdouterrin_setnull)), + "stdouterrin_setnull()", "" ]) super(PopenGateway, self)._remote_bootstrap_gateway(io, s) @@ -176,6 +151,46 @@ cmd += ' -i %s' % (identity,) cmdline.insert(0, cmd) super(SshGateway, self).__init__(' '.join(cmdline)) + + def _remote_bootstrap_gateway(self, io, s=""): + extra = "\n".join([ + str(py.code.Source(stdouterrin_setnull)), + "stdouterrin_setnull()", + s, + ]) + super(SshGateway, self)._remote_bootstrap_gateway(io, extra) + +def stdouterrin_setnull(): + # redirect file descriptors 0 and 1 to /dev/null, to avoid + # complete confusion (this is independent from the sys.stdout + # and sys.stderr redirection that gateway.remote_exec() can do) + # note that we redirect fd 2 on win too, since for some reason that + # blocks there, while it works (sending to stderr if possible else + # ignoring) on *nix + import sys, os + try: + devnull = os.devnull + except AttributeError: + if os.name == 'nt': + devnull = 'NUL' + else: + devnull = '/dev/null' + sys.stdin = os.fdopen(os.dup(0), 'rb', 0) + sys.stdout = os.fdopen(os.dup(1), 'wb', 0) + if os.name == 'nt': + sys.stderr = os.fdopen(os.dup(2), 'wb', 0) + fd = os.open(devnull, os.O_RDONLY) + os.dup2(fd, 0) + os.close(fd) + fd = os.open(devnull, os.O_WRONLY) + os.dup2(fd, 1) + if os.name == 'nt': + os.dup2(fd, 2) + os.close(fd) + +# XXX +# XXX unusued code below +# XXX class ExecGateway(PopenGateway): def remote_exec_sync_stdcapture(self, lines, callback): @@ -224,3 +239,4 @@ callback = self.callbacks[answerid] del self.callbacks[answerid] callback(value) + Modified: py/trunk/py/execnet/testing/test_gateway.py ============================================================================== --- py/trunk/py/execnet/testing/test_gateway.py (original) +++ py/trunk/py/execnet/testing/test_gateway.py Fri Feb 2 02:44:31 2007 @@ -25,6 +25,20 @@ (name, dottedname, olddottedname)) seen[name] = (dottedname, value) +def test_stdouterrin_setnull(): + cap = py.io.StdCaptureFD() + from py.__.execnet.register import stdouterrin_setnull + stdouterrin_setnull() + import os + os.write(1, "hello") + if os.name == "nt": + os.write(2, "world") + os.read(0, 1) + out, err = cap.reset() + assert not out + assert not err + + class TestMessage: def test_wire_protocol(self): for cls in gateway.Message._types.values(): @@ -177,12 +191,12 @@ # check that the both sides previous channels are really gone channel.waitclose(0.3) - assert channel.id not in self.gw._channelfactory._channels #assert c.id not in self.gw._channelfactory newchan = self.gw.remote_exec(''' assert %d not in channel.gateway._channelfactory._channels ''' % (channel.id)) newchan.waitclose(0.3) + assert channel.id not in self.gw._channelfactory._channels def test_channel_receiver_callback(self): l = [] @@ -454,9 +468,6 @@ py.test.skip("no known ssh target, use -S to set one") cls.gw = py.execnet.SshGateway(option.sshtarget) - def test_confusion_from_os_write_stdout(self): - py.test.skip("writing to FD 1 with SshGateways not supported yet") - def test_sshaddress(self): assert self.gw.remoteaddress == option.sshtarget From guido at codespeak.net Fri Feb 2 16:50:00 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Fri, 2 Feb 2007 16:50:00 +0100 (CET) Subject: [py-svn] r37812 - in py/trunk/py/apigen: . testing Message-ID: <20070202155000.681D710081@code0.codespeak.net> Author: guido Date: Fri Feb 2 16:49:58 2007 New Revision: 37812 Added: py/trunk/py/apigen/html.py Modified: py/trunk/py/apigen/htmlgen.py py/trunk/py/apigen/layout.py py/trunk/py/apigen/style.css py/trunk/py/apigen/testing/test_apigen_example.py Log: Using style.css from the doc directory for apigen now, with some overrides in a seperate stylesheet. Cleaned up the stylesheet and script handling a bit: stylesheets and scripts are now defined in the LayoutPage class (where they're easily customizable). Also removed some old unused code and moved the H class (html related) stuff to its own file. The idea is that this will become the place where all the layout is defined. Added: py/trunk/py/apigen/html.py ============================================================================== --- (empty file) +++ py/trunk/py/apigen/html.py Fri Feb 2 16:49:58 2007 @@ -0,0 +1,81 @@ + +from py.xml import html + +# HTML related stuff +class H(html): + class Content(html.div): + pass # style = html.Style(margin_left='15em') + + class Description(html.div): + pass + + class NamespaceDescription(Description): + pass + + class NamespaceItem(html.div): + pass + + class NamespaceDef(html.h1): + pass + + class ClassDescription(Description): + pass + + class ClassDef(html.h1): + pass + + class MethodDescription(Description): + pass + + class MethodDef(html.h2): + pass + + class FunctionDescription(Description): + pass + + class FunctionDef(html.h2): + pass + + class ParameterDescription(html.div): + pass + + class Docstring(html.pre): + style = html.Style(width='100%') + pass + + class Navigation(html.div): + #style = html.Style(min_height='99%', float='left', margin_top='1.2em', + # overflow='auto', width='15em', white_space='nowrap') + pass + + class NavigationItem(html.div): + pass + + class BaseDescription(html.a): + pass + + class SourceDef(html.div): + pass + + class NonPythonSource(html.pre): + pass # style = html.Style(margin_left='15em') + + class DirList(html.div): + pass # style = html.Style(margin_left='15em') + + class DirListItem(html.div): + pass + + class ValueDescList(html.ul): + def __init__(self, *args, **kwargs): + super(H.ValueDescList, self).__init__(*args, **kwargs) + + class ValueDescItem(html.li): + pass + + class CallStackDescription(Description): + pass + + class CallStackItem(html.div): + class_ = 'callstackitem' + Modified: py/trunk/py/apigen/htmlgen.py ============================================================================== --- py/trunk/py/apigen/htmlgen.py (original) +++ py/trunk/py/apigen/htmlgen.py Fri Feb 2 16:49:58 2007 @@ -8,6 +8,7 @@ from py.__.apigen.tracer.description import is_private from py.__.apigen.rest.genrest import split_of_last_part from py.__.apigen.linker import relpath +from py.__.apigen.html import H sorted = py.builtin.sorted html = py.xml.html @@ -45,83 +46,6 @@ ret.append(line[deindent:]) return '%s\n' % (linesep.join(ret),) -# HTML related stuff -class H(html): - class Content(html.div): - style = html.Style(margin_left='15em') - - class Description(html.div): - pass - - class NamespaceDescription(Description): - pass - - class NamespaceItem(html.div): - pass - - class NamespaceDef(html.h1): - pass - - class ClassDescription(Description): - pass - - class ClassDef(html.h1): - pass - - class MethodDescription(Description): - pass - - class MethodDef(html.h2): - pass - - class FunctionDescription(Description): - pass - - class FunctionDef(html.h2): - pass - - class ParameterDescription(html.div): - pass - - class Docstring(html.pre): - #style = html.Style(white_space='pre', min_height='3em') - pass - - class Navigation(html.div): - style = html.Style(min_height='99%', float='left', margin_top='1.2em', - overflow='auto', width='15em', white_space='nowrap') - - class NavigationItem(html.div): - pass - - class BaseDescription(html.a): - pass - - class SourceDef(html.div): - pass - - class NonPythonSource(html.pre): - style = html.Style(margin_left='15em') - - class DirList(html.div): - style = html.Style(margin_left='15em') - - class DirListItem(html.div): - pass - - class ValueDescList(html.ul): - def __init__(self, *args, **kwargs): - super(H.ValueDescList, self).__init__(*args, **kwargs) - - class ValueDescItem(html.li): - pass - - class CallStackDescription(Description): - pass - - class CallStackItem(html.div): - class_ = 'callstackitem' - def get_param_htmldesc(linker, func): """ get the html for the parameters of a function """ import inspect @@ -174,18 +98,11 @@ ret[ns].append(itempath) return ret -def wrap_page(project, title, contentel, navel, outputpath, stylesheeturl, - scripturls): +def wrap_page(project, title, contentel, navel, relbase, basepath): page = LayoutPage(project, title, nav=navel, encoding='UTF-8', - stylesheeturl=stylesheeturl, scripturls=scripturls) + relpath=relbase) page.set_content(contentel) - here = py.magic.autopath().dirpath() - style = here.join(stylesheeturl.split('/')[-1]).read() - outputpath.join(stylesheeturl.split('/')[-1]).write(style) - for spath in scripturls: - sname = spath.split('/')[-1] - sdata = here.join(sname).read() - outputpath.join(sname).write(sdata) + page.setup_scripts_styles(basepath) return page # the PageBuilder classes take care of producing the docs (using the stuff @@ -193,12 +110,9 @@ class AbstractPageBuilder(object): def write_page(self, title, reltargetpath, project, tag, nav): targetpath = self.base.join(reltargetpath) - stylesheeturl = relpath('%s%s' % (targetpath.dirpath(), os.path.sep), - self.base.join('style.css').strpath) - scripturls = [relpath('%s%s' % (targetpath.dirpath(), os.path.sep), - self.base.join('api.js').strpath)] - page = wrap_page(project, title, - tag, nav, self.base, stylesheeturl, scripturls) + relbase= relpath('%s%s' % (targetpath.dirpath(), targetpath.sep), + self.base.strpath + '/') + page = wrap_page(project, title, tag, nav, relbase, self.base) content = self.linker.call_withbase(reltargetpath, page.unicode) targetpath.ensure() targetpath.write(content.encode("utf8")) @@ -212,7 +126,7 @@ self.capture = capture def build_navigation(self, fspath): - nav = H.Navigation() + nav = H.Navigation(class_='sidebar') relpath = fspath.relto(self.projroot) path = relpath.split(os.path.sep) indent = 0 @@ -613,40 +527,7 @@ return navitems navitems += build_nav_level(dotted_name) - return H.Navigation(*navitems) - - - - navitems = [] - - # top namespace, index.html - module_name = self.dsa.get_module_name().split('/')[-1] - navitems.append(build_navitem_html(self.linker, module_name, '', 0, - (selection == ''))) - - indent = 1 - path = dotted_name.split('.') - if dotted_name != '': - # build html for each item in path to dotted_name item - for i in xrange(len(path)): - name = path[i] - item_dotted_name = '.'.join(path[:i+1]) - selected = (selection == item_dotted_name) - navitems.append(build_navitem_html(self.linker, name, - item_dotted_name, indent, - selected)) - indent += 1 - - # build sub items of dotted_name item - for item_dotted_name in py.builtin.sorted(item_dotted_names): - itemname = item_dotted_name.split('.')[-1] - if is_private(itemname): - continue - selected = (item_dotted_name == selection) - navitems.append(build_navitem_html(self.linker, itemname, - item_dotted_name, indent, - selected)) - return H.Navigation(*navitems) + return H.Navigation(class_='sidebar', *navitems) def build_callable_signature_description(self, dotted_name): args, retval = self.dsa.get_function_signature(dotted_name) Modified: py/trunk/py/apigen/layout.py ============================================================================== --- py/trunk/py/apigen/layout.py (original) +++ py/trunk/py/apigen/layout.py Fri Feb 2 16:49:58 2007 @@ -4,14 +4,19 @@ """ import py -from py.__.doc.confrest import Page +from py.__.doc import confrest +from py.__.apigen import linker -class LayoutPage(Page): +class LayoutPage(confrest.PyPage): """ this provides the layout and style information """ + stylesheets = [(py.path.local('../doc/style.css'), 'style.css'), + (py.path.local('style.css'), 'apigen_style.css')] + scripts = [(py.path.local('api.js'), 'api.js')] + def __init__(self, *args, **kwargs): self.nav = kwargs.pop('nav') - self.scripturls = kwargs.pop('scripturls', []) + self.relpath = kwargs.pop('relpath') super(LayoutPage, self).__init__(*args, **kwargs) def set_content(self, contentel): @@ -19,9 +24,19 @@ def fill(self): super(LayoutPage, self).fill() - self.menubar[:] = [] - self.menubar.append(self.nav) - for scripturl in self.scripturls: + #self.menubar[:] = [] + self.body.insert(0, self.nav) + + def setup_scripts_styles(self, copyto=None): + for path, name in self.stylesheets: + if copyto: + copyto.join(name).write(path.read()) + self.head.append(py.xml.html.link(type='text/css', + rel='stylesheet', + href=self.relpath + name)) + for path, name in self.scripts: + if copyto: + copyto.join(name).write(path.read()) self.head.append(py.xml.html.script(type="text/javascript", - src=scripturl)) + src=self.relpath + name)) Modified: py/trunk/py/apigen/style.css ============================================================================== --- py/trunk/py/apigen/style.css (original) +++ py/trunk/py/apigen/style.css Fri Feb 2 16:49:58 2007 @@ -1,12 +1,18 @@ -#sidebar { - width: 9em; - float: left; +div.sidebar { + font-family: Verdana, Helvetica, Arial, sans-serif; + width: 155px; vertical-align: top; margin-top: 0.5em; + position: absolute; + top: 130px; + left: 4px; + bottom: 4px; + overflow: auto; } -#main { - margin-left: 10em; +div.sidebar .selected a { + color: white; + background-color: #3ba6ec; } #content { @@ -15,47 +21,6 @@ width: 100%; } -#breadcrumb { - height: 5%; - display: none; -} - -#logo { - /* float: left; */ -} - -#logo img { - border-width: 0px; -} - -#menubar a { - text-decoration: none; - color: blue; -} - -#menubar div.selected a { - color: purple; -} - -.project_title { - font-size: 2em; - font-weight: bold; -} - -body { - background-color: #FFE; - color: black; -} - -body, div, p, h1, h2, h3, h4 { - font-family: Trebuchet MS, Verdana, Arial; -} - -a { - color: #006; - text-decoration: none; -} - ul { padding-left: 0em; margin-top: 0px; @@ -71,10 +36,6 @@ text-decoration: none; } -a:hover { - color: #005; -} - .lineno { text-align: right; color: #555; Modified: py/trunk/py/apigen/testing/test_apigen_example.py ============================================================================== --- py/trunk/py/apigen/testing/test_apigen_example.py (original) +++ py/trunk/py/apigen/testing/test_apigen_example.py Fri Feb 2 16:49:58 2007 @@ -204,6 +204,7 @@ print html run_string_sequence_test(html, [ 'href="../style.css"', + 'href="../apigen_style.css"', 'src="../api.js"', 'href="index.html">pkg', 'href="main.html">main', From hpk at codespeak.net Fri Feb 2 19:27:31 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 2 Feb 2007 19:27:31 +0100 (CET) Subject: [py-svn] r37815 - py/trunk/py/doc Message-ID: <20070202182731.AB9BD10060@code0.codespeak.net> Author: hpk Date: Fri Feb 2 19:27:22 2007 New Revision: 37815 Modified: py/trunk/py/doc/TODO.txt py/trunk/py/doc/path.txt Log: path.txt: showing a bit more for string methods update TODO Modified: py/trunk/py/doc/TODO.txt ============================================================================== --- py/trunk/py/doc/TODO.txt (original) +++ py/trunk/py/doc/TODO.txt Fri Feb 2 19:27:22 2007 @@ -37,10 +37,7 @@ * make "_" namespace: py.log -> py._log (pypy!) -* review py.io and write py.io.dupfile docstring - - (guido - added docstring, and code looks okay (what was the reason for the - review? anything specific?) +* (done mostly) review py.io and write py.io.dupfile docstring * re-consider what to do with read and write methods of py.path classes (since there are places that check for file-ness by doing hasattr(... 'write')) Modified: py/trunk/py/doc/path.txt ============================================================================== --- py/trunk/py/doc/path.txt (original) +++ py/trunk/py/doc/path.txt Fri Feb 2 19:27:22 2007 @@ -108,17 +108,18 @@ >>> subdir.join('textfile2.txt').write('spam eggs spam foo bar spam') >>> results = [] >>> for fpath in dirpath.visit('*.txt'): - ... if 'bar' in fpath.read(): - ... results.append(fpath.basename) + ... if 'bar' in fpath.read(): + ... results.append(fpath.basename) >>> results ['textfile1.txt', 'textfile2.txt', 'textfile2.txt'] -Joining path types +Working with Paths ++++++++++++++++++++ -This example shows the :api:`py.path` features to deal with actual paths -(strings). Note that the filesystem is never touched, all operations are -performed on a string level (so the paths don't have to exist, either):: +This example shows the :api:`py.path` features to deal with +filesystem paths Note that the filesystem is never touched, +all operations are performed on a string level (so the paths +don't have to exist, either):: >>> p1 = py.path.local('/foo/bar') >>> p2 = p1.join('baz/qux') @@ -130,6 +131,13 @@ >>> p3 = p1 / 'baz/qux' # the / operator allows joining, too >>> p2 == p3 True + >>> p4 = p1 + ".py" + >>> p4.basename == "bar.py" + True + >>> p4.ext == ".py" + True + >>> p4.purebasename == "bar" + True This should be possible on every implementation of :api:`py.path`, so regardless of whether the implementation wraps a UNIX filesystem, a Windows From hpk at codespeak.net Fri Feb 2 20:57:49 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 2 Feb 2007 20:57:49 +0100 (CET) Subject: [py-svn] r37822 - in py/trunk/py/execnet: . testing Message-ID: <20070202195749.8A8841006F@code0.codespeak.net> Author: hpk Date: Fri Feb 2 20:57:47 2007 New Revision: 37822 Modified: py/trunk/py/execnet/gateway.py py/trunk/py/execnet/register.py py/trunk/py/execnet/testing/test_gateway.py Log: added lots of docstrings, general cleanup Modified: py/trunk/py/execnet/gateway.py ============================================================================== --- py/trunk/py/execnet/gateway.py (original) +++ py/trunk/py/execnet/gateway.py Fri Feb 2 20:57:47 2007 @@ -33,23 +33,25 @@ class Gateway(object): _ThreadOut = ThreadOut remoteaddress = "" - - def __init__(self, io, startcount=2, maxthreads=None): + def __init__(self, io, execthreads=None, _startcount=2): + """ initialize core gateway, using the given + inputoutput object and 'execthreads' execution + threads. + """ global registered_cleanup - self._execpool = WorkerPool(maxthreads=maxthreads) -## self.running = True + self._execpool = WorkerPool(maxthreads=execthreads) self._io = io self._outgoing = Queue.Queue() - self._channelfactory = ChannelFactory(self, startcount) -## self._exitlock = threading.Lock() + self._channelfactory = ChannelFactory(self, _startcount) if not registered_cleanup: atexit.register(cleanup_atexit) registered_cleanup = True _active_sendqueues[self._outgoing] = True self._pool = NamedThreadPool(receiver = self._thread_receiver, - sender = self._thread_sender) + sender = self._thread_sender) def __repr__(self): + """ return string representing gateway type and status. """ addr = self.remoteaddress if addr: addr = '[%s]' % (addr,) @@ -199,13 +201,12 @@ return self._channelfactory.new() def remote_exec(self, source, stdout=None, stderr=None): - """ return channel object for communicating with the asynchronously - executing 'source' code which will have a corresponding 'channel' - object in its executing namespace. - - You may provide callback functions 'stdout' and 'stderr' - which will get called with the remote stdout/stderr output - piece by piece. + """ return channel object and connect it to a remote + execution thread where the given 'source' executes + and has the sister 'channel' object in its global + namespace. The callback functions 'stdout' and + 'stderr' get called on receival of remote + stdout/stderr output strings. """ try: source = str(Source(source)) @@ -252,29 +253,8 @@ c.waitclose(1.0) return Handle() -## def exit(self): -## """ initiate full gateway teardown. -## Note that the teardown of sender/receiver threads happens -## asynchronously and timeouts on stopping worker execution -## threads are ignored. You can issue join() or join(joinexec=False) -## if you want to wait for a full teardown (possibly excluding -## execution threads). -## """ -## # note that threads may still be scheduled to start -## # during our execution! -## self._exitlock.acquire() -## try: -## if self.running: -## self.running = False -## if not self._pool.getstarted('sender'): -## raise IOError("sender thread not alive anymore!") -## self._outgoing.put(None) -## self._trace("exit procedure triggered, pid %d " % (os.getpid(),)) -## _gateways.remove(self) -## finally: -## self._exitlock.release() - def exit(self): + """ Try to stop all IO activity. """ try: del _active_sendqueues[self._outgoing] except KeyError: @@ -283,6 +263,9 @@ self._outgoing.put(None) def join(self, joinexec=True): + """ Wait for all IO (and by default all execution activity) + to stop. + """ current = threading.currentThread() for x in self._pool.getstarted(): if x != current: Modified: py/trunk/py/execnet/register.py ============================================================================== --- py/trunk/py/execnet/register.py (original) +++ py/trunk/py/execnet/register.py Fri Feb 2 20:57:47 2007 @@ -7,9 +7,7 @@ # the list of modules that must be send to the other side # for bootstrapping gateways -# XXX we want to have a cleaner bootstrap mechanism -# by making sure early that we have the py lib available -# in a sufficient version +# XXX we'd like to have a leaner and meaner bootstrap mechanism startup_modules = [ 'py.__.thread.io', @@ -30,7 +28,7 @@ """ initialize gateways on both sides of a inputoutput object. """ def __init__(self, io): self._remote_bootstrap_gateway(io) - super(InstallableGateway, self).__init__(io=io, startcount=1) + super(InstallableGateway, self).__init__(io=io, _startcount=1) def _remote_bootstrap_gateway(self, io, extra=''): """ return Gateway with a asynchronously remotely @@ -42,7 +40,9 @@ """ bootstrap = [extra] bootstrap += [getsource(x) for x in startup_modules] - bootstrap += [io.server_stmt, "Gateway(io=io, startcount=2).join(joinexec=False)",] + bootstrap += [io.server_stmt, + "Gateway(io=io, _startcount=2).join(joinexec=False)", + ] source = "\n".join(bootstrap) self._trace("sending gateway bootstrap code") io.write('%r\n' % source) @@ -52,45 +52,20 @@ infile, outfile = os.popen2(cmd) io = inputoutput.Popen2IO(infile, outfile) super(PopenCmdGateway, self).__init__(io=io) -## self._pidchannel = self.remote_exec(""" -## import os -## channel.send(os.getpid()) -## """) - -## def exit(self): -## try: -## self._pidchannel.waitclose(timeout=0.5) -## pid = self._pidchannel.receive() -## except IOError: -## self._trace("IOError: could not receive child PID:") -## self._traceex(sys.exc_info()) -## pid = None -## super(PopenCmdGateway, self).exit() -## if pid is not None: -## self._trace("waiting for pid %s" % pid) -## try: -## os.waitpid(pid, 0) -## except KeyboardInterrupt: -## if sys.platform != "win32": -## os.kill(pid, 15) -## raise -## except OSError, e: -## self._trace("child process %s already dead? error:%s" % -## (pid, str(e))) class PopenGateway(PopenCmdGateway): - # use sysfind/sysexec/subprocess instead of os.popen? + """ This Gateway provides interaction with a newly started + python subprocess. + """ def __init__(self, python=sys.executable): + """ instantiate a gateway to a subprocess + started with the given 'python' executable. + """ cmd = '%s -u -c "exec input()"' % python super(PopenGateway, self).__init__(cmd) def _remote_bootstrap_gateway(self, io, extra=''): - # XXX the following hack helps us to import the same version - # of the py lib and other dependcies, but only works for - # PopenGateways because we can assume to have access to - # the same filesystem - # --> we definitely need proper remote imports working - # across any kind of gateway! + # have the subprocess use the same PYTHONPATH and py lib x = py.path.local(py.__file__).dirpath().dirpath() ppath = os.environ.get('PYTHONPATH', '') plist = [str(x)] + ppath.split(':') @@ -98,48 +73,66 @@ "import sys ; sys.path[:0] = %r" % (plist,), "import os ; os.environ['PYTHONPATH'] = %r" % ppath, str(py.code.Source(stdouterrin_setnull)), - "stdouterrin_setnull()", + "stdouterrin_setnull()", "" ]) super(PopenGateway, self)._remote_bootstrap_gateway(io, s) class SocketGateway(InstallableGateway): + """ This Gateway provides interaction with a remote process + by connecting to a specified socket. On the remote + side you need to manually start a small script + (py/execnet/script/socketserver.py) that accepts + SocketGateway connections. + """ def __init__(self, host, port): - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + """ instantiate a gateway to a processed accessed + via a host/port specified socket. + """ self.host = host = str(host) self.port = port = int(port) + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((host, port)) io = inputoutput.SocketIO(sock) super(SocketGateway, self).__init__(io=io) self.remoteaddress = '%s:%d' % (self.host, self.port) - def remote_install(cls, gateway, hostport=None): - """ return a connected socket gateway through the - given gateway. + def new_remote(cls, gateway, hostport=None): + """ return a new (connected) socket gateway, instatiated + indirectly through the given 'gateway'. """ if hostport is None: host, port = ('', 0) # XXX works on all platforms? else: host, port = hostport socketserverbootstrap = py.code.Source( - mypath.dirpath('script', 'socketserver.py').read('rU'), - """ + mypath.dirpath('script', 'socketserver.py').read('rU'), """ import socket sock = bind_and_listen((%r, %r)) port = sock.getsockname() channel.send(port) startserver(sock) - """ % (host, port)) + """ % (host, port) + ) # execute the above socketserverbootstrap on the other side channel = gateway.remote_exec(socketserverbootstrap) (realhost, realport) = channel.receive() - #gateway._trace("remote_install received" - # "port=%r, hostname = %r" %(realport, hostname)) + #gateway._trace("new_remote received" + # "port=%r, hostname = %r" %(realport, hostname)) return py.execnet.SocketGateway(host, realport) - remote_install = classmethod(remote_install) + new_remote = classmethod(new_remote) + class SshGateway(PopenCmdGateway): + """ This Gateway provides interaction with a remote process, + established via the 'ssh' command line binary. + The remote side needs to have a Python interpreter executable. + """ def __init__(self, sshaddress, remotepython='python', identity=None): + """ instantiate a remote ssh process with the + given 'sshaddress' and remotepython version. + you may specify an 'identity' filepath. + """ self.remoteaddress = sshaddress remotecmd = '%s -u -c "exec input()"' % (remotepython,) cmdline = [sshaddress, remotecmd] @@ -160,8 +153,11 @@ ]) super(SshGateway, self)._remote_bootstrap_gateway(io, extra) + def stdouterrin_setnull(): - # redirect file descriptors 0 and 1 to /dev/null, to avoid + """ redirect file descriptors 0 and 1 (and possibly 2) to /dev/null. + note that this function may run remotely without py lib support. + """ # complete confusion (this is independent from the sys.stdout # and sys.stderr redirection that gateway.remote_exec() can do) # note that we redirect fd 2 on win too, since for some reason that @@ -188,55 +184,3 @@ os.dup2(fd, 2) os.close(fd) -# XXX -# XXX unusued code below -# XXX - -class ExecGateway(PopenGateway): - def remote_exec_sync_stdcapture(self, lines, callback): - # hack: turn the content of the cell into - # - # if 1: - # line1 - # line2 - # ... - # - lines = [' ' + line for line in lines] - lines.insert(0, 'if 1:') - lines.append('') - sourcecode = '\n'.join(lines) - try: - callbacks = self.callbacks - except AttributeError: - callbacks = self.callbacks = {} - answerid = id(callback) - self.callbacks[answerid] = callback - - self.exec_remote(''' - import sys, StringIO - try: - execns - except: - execns = {} - oldout, olderr = sys.stdout, sys.stderr - try: - buffer = StringIO.StringIO() - sys.stdout = sys.stderr = buffer - try: - exec compile(%(sourcecode)r, 'single') in execns - except: - import traceback - traceback.print_exc() - finally: - sys.stdout=oldout - sys.stderr=olderr - # fiddle us (the caller) into executing the callback on remote answers - gateway.exec_remote( - "gateway.invoke_callback(%(answerid)r, %%r)" %% buffer.getvalue()) - ''' % locals()) - - def invoke_callback(self, answerid, value): - callback = self.callbacks[answerid] - del self.callbacks[answerid] - callback(value) - Modified: py/trunk/py/execnet/testing/test_gateway.py ============================================================================== --- py/trunk/py/execnet/testing/test_gateway.py (original) +++ py/trunk/py/execnet/testing/test_gateway.py Fri Feb 2 20:57:47 2007 @@ -302,10 +302,11 @@ def test_remote_exec_redirect_multi(self): num = 3 l = [[] for x in range(num)] - channels = [self.gw.remote_exec("print %d" % i, stdout=l[i].append) + channels = [self.gw.remote_exec("print %d" % i, + stdout=l[i].append) for i in range(num)] for x in channels: - x.waitclose(1.0) + x.waitclose(5.0) for i in range(num): subl = l[i] @@ -451,9 +452,9 @@ def setup_class(cls): # open a gateway to a fresh child process cls.proxygw = py.execnet.PopenGateway() - cls.gw = py.execnet.SocketGateway.remote_install(cls.proxygw, - ("127.0.0.1", 0) - ) + cls.gw = py.execnet.SocketGateway.new_remote(cls.proxygw, + ("127.0.0.1", 0) + ) ## def teardown_class(cls): ## cls.gw.exit() From hpk at codespeak.net Fri Feb 2 20:59:11 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 2 Feb 2007 20:59:11 +0100 (CET) Subject: [py-svn] r37824 - py/trunk/py/execnet Message-ID: <20070202195911.9F4AE1007B@code0.codespeak.net> Author: hpk Date: Fri Feb 2 20:59:10 2007 New Revision: 37824 Modified: py/trunk/py/execnet/register.py Log: fix typo Modified: py/trunk/py/execnet/register.py ============================================================================== --- py/trunk/py/execnet/register.py (original) +++ py/trunk/py/execnet/register.py Fri Feb 2 20:59:10 2007 @@ -86,7 +86,7 @@ SocketGateway connections. """ def __init__(self, host, port): - """ instantiate a gateway to a processed accessed + """ instantiate a gateway to a process accessed via a host/port specified socket. """ self.host = host = str(host) From hpk at codespeak.net Fri Feb 2 21:58:01 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 2 Feb 2007 21:58:01 +0100 (CET) Subject: [py-svn] r37830 - py/trunk/py/execnet Message-ID: <20070202205801.C89221005A@code0.codespeak.net> Author: hpk Date: Fri Feb 2 21:57:59 2007 New Revision: 37830 Modified: py/trunk/py/execnet/rsync.py Log: better RSync docstring Modified: py/trunk/py/execnet/rsync.py ============================================================================== --- py/trunk/py/execnet/rsync.py (original) +++ py/trunk/py/execnet/rsync.py Fri Feb 2 21:57:59 2007 @@ -3,18 +3,17 @@ class RSync(object): - """ This is an example usage of py.execnet - a sample RSync - protocol, which can perform syncing 1-to-n. + """ This class allows to synchronise files and directories + with one or multiple remote filesystems. - Sample usage: you instantiate this class, eventually providing a - callback when rsyncing is done, than add some targets - (gateway + destdir) by running add_target and finally - invoking send() which will send provided source tree remotely. - - There is limited support for symlinks, which means that symlinks - pointing to the sourcetree will be send "as is" while external - symlinks will be just copied (regardless of existance of such - a path on remote side) + An RSync instance allows to dynamically add remote targets + and then synchronizes the remote filesystems with + any provided source directory. + + There is limited support for symlinks, which means that symlinks + pointing to the sourcetree will be send "as is" while external + symlinks will be just copied (regardless of existance of such + a path on remote side). """ def __init__(self, callback=None, **options): for name in options: From hpk at codespeak.net Fri Feb 2 22:01:28 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 2 Feb 2007 22:01:28 +0100 (CET) Subject: [py-svn] r37831 - py/trunk/py/execnet Message-ID: <20070202210128.F22E410060@code0.codespeak.net> Author: hpk Date: Fri Feb 2 22:01:27 2007 New Revision: 37831 Modified: py/trunk/py/execnet/rsync.py Log: improving more docstrings Modified: py/trunk/py/execnet/rsync.py ============================================================================== --- py/trunk/py/execnet/rsync.py (original) +++ py/trunk/py/execnet/rsync.py Fri Feb 2 22:01:27 2007 @@ -28,7 +28,8 @@ return True def add_target(self, gateway, destdir, finishedcallback=None): - """ Adds a target for to-be-send data + """ Adds a remote target specified via a 'gateway' + and a remote destination directory. """ def itemcallback(req): self.receivequeue.put((channel, req)) @@ -38,7 +39,7 @@ self.channels[channel] = finishedcallback def send(self, sourcedir): - """ Sends a sourcedir to previously prepared targets + """ Sends a sourcedir to all added targets. """ self.sourcedir = str(sourcedir) # normalize a trailing '/' away From guido at codespeak.net Fri Feb 2 22:47:23 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Fri, 2 Feb 2007 22:47:23 +0100 (CET) Subject: [py-svn] r37833 - py/trunk/py/apigen Message-ID: <20070202214723.20C6F10061@code0.codespeak.net> Author: guido Date: Fri Feb 2 22:47:21 2007 New Revision: 37833 Modified: py/trunk/py/apigen/layout.py Log: Oops, was using py.path.local('.') instead of py.magic.autopath().dirpath() to find the scripts to copy... Modified: py/trunk/py/apigen/layout.py ============================================================================== --- py/trunk/py/apigen/layout.py (original) +++ py/trunk/py/apigen/layout.py Fri Feb 2 22:47:21 2007 @@ -7,12 +7,14 @@ from py.__.doc import confrest from py.__.apigen import linker +here = py.magic.autopath().dirpath() + class LayoutPage(confrest.PyPage): """ this provides the layout and style information """ - stylesheets = [(py.path.local('../doc/style.css'), 'style.css'), - (py.path.local('style.css'), 'apigen_style.css')] - scripts = [(py.path.local('api.js'), 'api.js')] + stylesheets = [(here.join('../doc/style.css'), 'style.css'), + (here.join('style.css'), 'apigen_style.css')] + scripts = [(here.join('api.js'), 'api.js')] def __init__(self, *args, **kwargs): self.nav = kwargs.pop('nav') From hpk at codespeak.net Sat Feb 3 00:09:32 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 3 Feb 2007 00:09:32 +0100 (CET) Subject: [py-svn] r37834 - py/trunk/py/doc Message-ID: <20070202230932.62D3710060@code0.codespeak.net> Author: hpk Date: Sat Feb 3 00:09:29 2007 New Revision: 37834 Modified: py/trunk/py/doc/execnet.txt Log: reworked execnet intro to more explicitely state the main (high-level) features. Modified: py/trunk/py/doc/execnet.txt ============================================================================== --- py/trunk/py/doc/execnet.txt (original) +++ py/trunk/py/doc/execnet.txt Sat Feb 3 00:09:29 2007 @@ -4,29 +4,29 @@ .. contents:: .. sectnum:: -``py.execnet`` deals with letting your python programs execute and -communicate across process and computer barriers. At the -core it is a very simple and powerful mechanism: executing -source code at "the other side" and communicating with -remote parts of your program. - -A warning note: We are doing documentation-driven development -in some ways. So some of the described features are not -there yet. You may refer to the `py API`_ reference for -further information. - - A new view on distributed execution ----------------------------------- -**py.execnet** lets you asynchronously execute source code on -remote places. The sending and receiving side communicate via -Channels that transport marshallable objects. A core feature -of **py.execnet** is that **the communication protocols can be -completely defined by the client side**. Usually, with -server/client apps and especially Remote Method Based (RMI) -approaches you have to define interfaces and have to -upgrade your server and client and restart both. +``py.execnet`` supports ad-hoc distribution of parts of +a program across process and network barriers. *Ad-hoc* +means that the client side may completely control + +* which parts of a program execute remotely and + +* which data protocols are used between them + +without requiring any prior manual installation +of user program code on the remote side. In fact, +not even a prior installation of any server code +is required, provided there is a way to get +an input/output connection to a python interpreter +(for example via "ssh" and a "python" executable). + +By comparison, traditional Remote Method Based (RMI) +require prior installation and manual rather +heavy processes of setup, distribution and +communication between program parts. + What about Security? Are you completely nuts? --------------------------------------------- From hpk at codespeak.net Sat Feb 3 00:10:42 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 3 Feb 2007 00:10:42 +0100 (CET) Subject: [py-svn] r37835 - py/trunk/py/doc Message-ID: <20070202231042.677D810061@code0.codespeak.net> Author: hpk Date: Sat Feb 3 00:10:38 2007 New Revision: 37835 Modified: py/trunk/py/doc/TODO.txt Log: some updates to TODO items Modified: py/trunk/py/doc/TODO.txt ============================================================================== --- py/trunk/py/doc/TODO.txt (original) +++ py/trunk/py/doc/TODO.txt Sat Feb 3 00:10:38 2007 @@ -57,7 +57,7 @@ * optional: support setuptools (eggs?) installs, and instally from pypi (and register pylib there) -* see if things work on Win32 (partially done) +* (DONE/c-modules don't) see if things work on Win32 (partially done) * refine and implement `releasescheme`_ @@ -81,10 +81,6 @@ testing ----------- -* windows tests (rev 36514 passes without errors, many skips) - - (guido tested again, and fixed newly broken tests, all work again now) - * these should all work on 0.9 and on the py lib and pypy: - running "py.test -s" - running "py.test --pdb" @@ -94,7 +90,7 @@ (guido tested all on win32, everything works except --dist (requires os.fork to work)) -* see why startcapture() used to not use FD-based +* (DONE) see why startcapture() used to not use FD-based "py.io.StdCaptureFD" to isolate standard output. use that check if all py and PyPy tests pass as good as they do without. From guido at codespeak.net Sat Feb 3 00:29:05 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Sat, 3 Feb 2007 00:29:05 +0100 (CET) Subject: [py-svn] r37837 - in py/trunk/py/apigen: . testing Message-ID: <20070202232905.9265410071@code0.codespeak.net> Author: guido Date: Sat Feb 3 00:29:01 2007 New Revision: 37837 Modified: py/trunk/py/apigen/html.py py/trunk/py/apigen/htmlgen.py py/trunk/py/apigen/testing/test_apigen_example.py py/trunk/py/apigen/testing/test_htmlgen.py Log: A bit of cleanup of the HTML generation: some of the layout related stuff is moved to the classes in the H namespace. This hopefully (when done, more can definitely be done later) seperates the HTML generation better from the presentation-related code. Modified: py/trunk/py/apigen/html.py ============================================================================== --- py/trunk/py/apigen/html.py (original) +++ py/trunk/py/apigen/html.py Sat Feb 3 00:29:01 2007 @@ -31,10 +31,22 @@ pass class FunctionDescription(Description): - pass + def __init__(self, localname, argdesc, docstring, valuedesc, csource, + callstack): + fd = H.FunctionDef(localname, argdesc) + ds = H.Docstring(docstring or '*no docstring available*') + fi = H.FunctionInfo(valuedesc, csource, callstack) + super(H.FunctionDescription, self).__init__(fd, ds, fi) class FunctionDef(html.h2): - pass + def __init__(self, name, argdesc): + super(H.FunctionDef, self).__init__('def %s%s:' % (name, argdesc)) + + class FunctionInfo(html.div): + def __init__(self, valuedesc, csource, callstack): + super(H.FunctionInfo, self).__init__( + H.Hideable('funcinfo', 'funcinfo', valuedesc, csource, + callstack)) class ParameterDescription(html.div): pass @@ -49,11 +61,26 @@ pass class NavigationItem(html.div): - pass + def __init__(self, linker, linkid, name, indent, selected): + href = linker.get_lazyhref(linkid) + super(H.NavigationItem, self).__init__((indent * 2 * u'\xa0'), + H.a(name, href=href)) + if selected: + self.attr.class_ = 'selected' class BaseDescription(html.a): pass + class SourceSnippet(html.div): + def __init__(self, text, href, sourceels=None): + if sourceels is None: + sourceels = [] + link = text + if href: + link = H.a(text, href=href) + super(H.SourceSnippet, self).__init__( + link, H.div(class_='code', *sourceels)) + class SourceDef(html.div): pass @@ -74,8 +101,24 @@ pass class CallStackDescription(Description): - pass + def __init__(self, callstackdiv): + super(H.CallStackDescription, self).__init__( + H.Hideable('callsites', 'callsites', csdiv)) class CallStackItem(html.div): - class_ = 'callstackitem' + def __init__(self, filename, lineno, traceback): + super(H.CallStackItem, self).__init__( + H.Hideable("stack trace %s - line %s" % (filename, lineno), + 'callstackitem', traceback)) + + class Hideable(html.div): + def __init__(self, name, class_, *content): + super(H.Hideable, self).__init__( + H.div(H.a('show/hide %s' % (name,), + href='#', + onclick=('showhideel(getnextsibling(this));' + 'return false;')), + H.div(style='display: none', + class_=class_, + *content))) Modified: py/trunk/py/apigen/htmlgen.py ============================================================================== --- py/trunk/py/apigen/htmlgen.py (original) +++ py/trunk/py/apigen/htmlgen.py Sat Feb 3 00:29:01 2007 @@ -50,14 +50,7 @@ """ get the html for the parameters of a function """ import inspect # XXX copy and modify formatargspec to produce html - return H.em(inspect.formatargspec(*inspect.getargspec(func))) - -def build_navitem_html(linker, name, linkid, indent, selected): - href = linker.get_lazyhref(linkid) - navitem = H.NavigationItem((indent * 2 * u'\xa0'), H.a(name, href=href)) - if selected: - navitem.attr.class_ = 'selected' - return navitem + return inspect.formatargspec(*inspect.getargspec(func)) # some helper functionality def source_dirs_files(fspath): @@ -139,15 +132,15 @@ text = self.projroot.basename else: text = path[i-1] - nav.append(build_navitem_html(self.linker, text, abspath, - indent, False)) + nav.append(H.NavigationItem(self.linker, abspath, text, + indent, False)) indent += 1 # build siblings or children and self if fspath.check(dir=True): # we're a dir, build ourselves and our children dirpath = fspath - nav.append(build_navitem_html(self.linker, dirpath.basename, - dirpath.strpath, indent, True)) + nav.append(H.NavigationItem(self.linker, dirpath.strpath, + dirpath.basename, indent, True)) indent += 1 elif fspath.strpath == self.projroot.strpath: dirpath = fspath @@ -156,13 +149,13 @@ dirpath = fspath.dirpath() diritems, fileitems = source_dirs_files(dirpath) for dir in diritems: - nav.append(build_navitem_html(self.linker, dir.basename, - dir.strpath, indent, False)) + nav.append(H.NavigationItem(self.linker, dir.strpath, dir.basename, + indent, False)) for file in fileitems: selected = (fspath.check(file=True) and file.basename == fspath.basename) - nav.append(build_navitem_html(self.linker, file.basename, - file.strpath, indent, selected)) + nav.append(H.NavigationItem(self.linker, file.strpath, + file.basename, indent, selected)) return nav re = py.std.re @@ -232,6 +225,7 @@ except (KeyboardInterrupt, SystemError): raise except: # XXX strange stuff going wrong at times... need to fix + raise exc, e, tb = py.std.sys.exc_info() print '%s - %s' % (exc, e) print @@ -288,41 +282,26 @@ callable_source = self.dsa.get_function_source(dotted_name) # i assume they're both either available or unavailable(XXX ?) is_in_pkg = self.is_in_pkg(sourcefile) + href = None + text = 'could not get to source file' + colored = [] if sourcefile and callable_source: enc = source_html.get_module_encoding(sourcefile) tokenizer = source_color.Tokenizer(source_color.PythonSchema) firstlineno = func.func_code.co_firstlineno org = callable_source.split('\n') colored = enumerate_and_color(org, firstlineno, enc) + text = 'source: %s' % (sourcefile,) if is_in_pkg: - slink = H.a('source: %s' % (sourcefile,), - href=self.linker.get_lazyhref(sourcefile)) - else: - slink = H.em('source: %s' % (sourcefile,)) - csource = H.div(H.br(), slink, H.br(), - H.SourceDef(H.div(class_='code', *colored))) - else: - csource = H.SourceDef('could not get source file') + href = self.linker.get_lazyhref(sourcefile) - csdiv = H.div(style='display: none') - for cs, _ in self.dsa.get_function_callpoints(dotted_name): - csdiv.append(self.build_callsite(dotted_name, cs)) - callstack = H.CallStackDescription( - H.a('show/hide call sites', - href='#', - onclick='showhideel(getnextsibling(this)); return false;'), - csdiv, - ) - snippet = H.FunctionDescription( - H.FunctionDef('def %s' % (localname,), argdesc), - H.Docstring(docstring or '*no docstring available*'), - H.div(H.a('show/hide info', - href='#', - onclick=('showhideel(getnextsibling(this));' - 'return false;')), - H.div(valuedesc, csource, callstack, style='display: none', - class_='funcinfo')), - ) + csource = H.SourceSnippet(text, href, colored) + callstack = self.dsa.get_function_callpoints(dotted_name) + csitems = [] + for cs, _ in callstack: + csitems.append(self.build_callsite(dotted_name, cs)) + snippet = H.FunctionDescription(localname, argdesc, docstring, + valuedesc, csource, csitems) return snippet @@ -501,8 +480,8 @@ # top namespace, index.html module_name = self.dsa.get_module_name().split('/')[-1] - navitems.append(build_navitem_html(self.linker, module_name, '', 0, - True)) + navitems.append(H.NavigationItem(self.linker, '', module_name, 0, + True)) def build_nav_level(dotted_name, depth=1): navitems = [] path = dotted_name.split('.')[:depth] @@ -513,8 +492,8 @@ sibname = sibpath[-1] if is_private(sibname): continue - navitems.append(build_navitem_html(self.linker, sibname, - dn, depth, selected)) + navitems.append(H.NavigationItem(self.linker, dn, sibname, + depth, selected)) if selected: lastlevel = dn.count('.') == dotted_name.count('.') if not lastlevel: @@ -581,15 +560,10 @@ return py.path.local(sourcefile).relto(self.projpath) def build_callsite(self, functionname, call_site): + print 'building callsite for', functionname tbtag = self.gen_traceback(functionname, reversed(call_site)) - tag = H.CallStackItem( - H.a("show/hide stack trace %s - line %s" % ( - call_site[0].filename, call_site[0].lineno + 1), - href='#', - onclick="showhideel(getnextsibling(this)); return false;"), - H.div(tbtag, style='display: none', class_='callstackitem'), - ) - return tag + return H.CallStackItem(call_site[0].filename, call_site[0].lineno + 1, + tbtag) _reg_source = py.std.re.compile(r'([^>]*)<(.*)>') def gen_traceback(self, funcname, call_site): Modified: py/trunk/py/apigen/testing/test_apigen_example.py ============================================================================== --- py/trunk/py/apigen/testing/test_apigen_example.py (original) +++ py/trunk/py/apigen/testing/test_apigen_example.py Sat Feb 3 00:29:01 2007 @@ -364,6 +364,16 @@ '

files

']) _checkhtml(html) + def test_build_source_page(self): + data = self.spb.prepare_pages(self.fs_root) + self.spb.build_pages(data, self.project, self.fs_root) + funcsource = self.base.join('source/pkg/func.py.html') + assert funcsource.check(file=True) + html = funcsource.read() + print html + assert ('def ' + 'func(arg1):') in html + def test_build_navigation_root(self): self.spb.prepare_pages(self.fs_root) nav = self.spb.build_navigation(self.fs_root.join('pkg')) Modified: py/trunk/py/apigen/testing/test_htmlgen.py ============================================================================== --- py/trunk/py/apigen/testing/test_htmlgen.py (original) +++ py/trunk/py/apigen/testing/test_htmlgen.py Sat Feb 3 00:29:01 2007 @@ -19,15 +19,6 @@ 'pkg': ['pkg.sub', 'pkg.SomeClass', 'pkg.SomeSubClass']} -def test_build_navitem_html(): - l = Linker() - l.set_link('spam.eggs.foo', 'foo.html') - h = htmlgen.build_navitem_html(l, 'foo', 'spam.eggs.foo', 0, False) - assert unicode(h) == u'' - h = htmlgen.build_navitem_html(l, 'bar', 'spam.eggs.foo', 1, True) - assert unicode(h) == (u'
\xa0\xa0' - u'bar
') - def test_source_dirs_files(): temp = py.test.ensuretemp('test_source_dirs_files') temp.join('dir').ensure(dir=True) @@ -52,3 +43,15 @@ assert htmlgen.deindent('foo\n\n bar\n baz\n') == ( 'foo\n\n bar\nbaz\n') +def test_enumerate_and_color(): + colored = htmlgen.enumerate_and_color(['def foo():', ' print "bar"'], 0, + 'ascii') + div = py.xml.html.div(*colored).unicode(indent=0) + assert div == ('
' + ' 1: ' + 'def foo():\n' + ' 2: ' + ' print' + ' "bar"\n' + '
') + From fijal at codespeak.net Sat Feb 3 00:32:37 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Sat, 3 Feb 2007 00:32:37 +0100 (CET) Subject: [py-svn] r37838 - py/trunk/py/execnet Message-ID: <20070202233237.982DC10071@code0.codespeak.net> Author: fijal Date: Sat Feb 3 00:32:35 2007 New Revision: 37838 Modified: py/trunk/py/execnet/rsync.py Log: Privatize attributes of rsync.py Modified: py/trunk/py/execnet/rsync.py ============================================================================== --- py/trunk/py/execnet/rsync.py (original) +++ py/trunk/py/execnet/rsync.py Sat Feb 3 00:32:35 2007 @@ -18,11 +18,11 @@ def __init__(self, callback=None, **options): for name in options: assert name in ('delete') - self.options = options - self.callback = callback - self.channels = {} - self.receivequeue = Queue() - self.links = [] + self._options = options + self._callback = callback + self._channels = {} + self._receivequeue = Queue() + self._links = [] def filter(self, path): return True @@ -32,32 +32,32 @@ and a remote destination directory. """ def itemcallback(req): - self.receivequeue.put((channel, req)) + self._receivequeue.put((channel, req)) channel = gateway.remote_exec(REMOTE_SOURCE) channel.setcallback(itemcallback, endmarker = None) - channel.send((str(destdir), self.options)) - self.channels[channel] = finishedcallback + channel.send((str(destdir), self._options)) + self._channels[channel] = finishedcallback def send(self, sourcedir): """ Sends a sourcedir to all added targets. """ - self.sourcedir = str(sourcedir) + self._sourcedir = str(sourcedir) # normalize a trailing '/' away - self.sourcedir = os.path.dirname(os.path.join(self.sourcedir, 'x')) + self._sourcedir = os.path.dirname(os.path.join(self._sourcedir, 'x')) # send directory structure and file timestamps/sizes - self._send_directory_structure(self.sourcedir) + self._send_directory_structure(self._sourcedir) # paths and to_send are only used for doing # progress-related callbacks - self.paths = {} - self.to_send = {} + self._paths = {} + self._to_send = {} # send modified file to clients - while self.channels: - channel, req = self.receivequeue.get() + while self._channels: + channel, req = self._receivequeue.get() if req is None: # end-of-channel - if channel in self.channels: + if channel in self._channels: # too early! we must have got an error channel.waitclose() # or else we raise one @@ -66,25 +66,25 @@ else: command, data = req if command == "links": - for link in self.links: + for link in self._links: channel.send(link) # completion marker, this host is done channel.send(42) elif command == "done": - finishedcallback = self.channels.pop(channel) + finishedcallback = self._channels.pop(channel) if finishedcallback: finishedcallback() elif command == "ack": - if self.callback: - self.callback("ack", self.paths[data], channel) + if self._callback: + self._callback("ack", self._paths[data], channel) elif command == "list_done": # sum up all to send - if self.callback: - s = sum([self.paths[i] for i in self.to_send[channel]]) - self.callback("list", s, channel) + if self._callback: + s = sum([self._paths[i] for i in self._to_send[channel]]) + self._callback("list", s, channel) elif command == "send": modified_rel_path, checksum = data - modifiedpath = os.path.join(self.sourcedir, *modified_rel_path) + modifiedpath = os.path.join(self._sourcedir, *modified_rel_path) try: f = open(modifiedpath, 'rb') data = f.read() @@ -94,12 +94,12 @@ # provide info to progress callback function modified_rel_path = "/".join(modified_rel_path) if data is not None: - self.paths[modified_rel_path] = len(data) + self._paths[modified_rel_path] = len(data) else: - self.paths[modified_rel_path] = 0 - if channel not in self.to_send: - self.to_send[channel] = [] - self.to_send[channel].append(modified_rel_path) + self._paths[modified_rel_path] = 0 + if channel not in self._to_send: + self._to_send[channel] = [] + self._to_send[channel].append(modified_rel_path) if data is not None: f.close() @@ -118,11 +118,11 @@ assert "Unknown command %s" % command def _broadcast(self, msg): - for channel in self.channels: + for channel in self._channels: channel.send(msg) def _send_link(self, basename, linkpoint): - self.links.append(("link", basename, linkpoint)) + self._links.append(("link", basename, linkpoint)) def _send_directory_structure(self, path): try: @@ -147,13 +147,13 @@ self._send_directory_structure(p) elif stat.S_ISLNK(st.st_mode): linkpoint = os.readlink(path) - basename = path[len(self.sourcedir) + 1:] + basename = path[len(self._sourcedir) + 1:] if not linkpoint.startswith(os.sep): # relative link, just send it # XXX: do sth with ../ links self._send_link(basename, linkpoint) - elif linkpoint.startswith(self.sourcedir): - self._send_link(basename, linkpoint[len(self.sourcedir) + 1:]) + elif linkpoint.startswith(self._sourcedir): + self._send_link(basename, linkpoint[len(self._sourcedir) + 1:]) else: self._send_link(basename, linkpoint) self._broadcast(None) From fijal at codespeak.net Sat Feb 3 11:26:24 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Sat, 3 Feb 2007 11:26:24 +0100 (CET) Subject: [py-svn] r37850 - py/trunk/py/execnet Message-ID: <20070203102624.EA94010069@code0.codespeak.net> Author: fijal Date: Sat Feb 3 11:26:23 2007 New Revision: 37850 Modified: py/trunk/py/execnet/rsync.py Log: Split methods a bit to smaller parts. Modified: py/trunk/py/execnet/rsync.py ============================================================================== --- py/trunk/py/execnet/rsync.py (original) +++ py/trunk/py/execnet/rsync.py Sat Feb 3 11:26:23 2007 @@ -38,6 +38,67 @@ channel.send((str(destdir), self._options)) self._channels[channel] = finishedcallback + def _end_of_channel(self, channel): + if channel in self._channels: + # too early! we must have got an error + channel.waitclose() + # or else we raise one + raise IOError('connection unexpectedly closed: %s ' % ( + channel.gateway,)) + + def _process_link(self, channel): + for link in self._links: + channel.send(link) + # completion marker, this host is done + channel.send(42) + + def _done(self, channel): + """ Call all callbacks + """ + finishedcallback = self._channels.pop(channel) + if finishedcallback: + finishedcallback() + + def _list_done(self, channel): + # sum up all to send + if self._callback: + s = sum([self._paths[i] for i in self._to_send[channel]]) + self._callback("list", s, channel) + + def _send_item(self, channel, data): + """ Send one item + """ + modified_rel_path, checksum = data + modifiedpath = os.path.join(self._sourcedir, *modified_rel_path) + try: + f = open(modifiedpath, 'rb') + data = f.read() + except IOError: + data = None + + # provide info to progress callback function + modified_rel_path = "/".join(modified_rel_path) + if data is not None: + self._paths[modified_rel_path] = len(data) + else: + self._paths[modified_rel_path] = 0 + if channel not in self._to_send: + self._to_send[channel] = [] + self._to_send[channel].append(modified_rel_path) + + if data is not None: + f.close() + if checksum is not None and checksum == md5.md5(data).digest(): + data = None # not really modified + else: + # ! there is a reason for the interning: + # sharing multiple copies of the file's data + data = intern(data) + print '%s <= %s' % ( + channel.gateway.remoteaddress, + modified_rel_path) + channel.send(data) + def send(self, sourcedir): """ Sends a sourcedir to all added targets. """ @@ -56,63 +117,20 @@ while self._channels: channel, req = self._receivequeue.get() if req is None: - # end-of-channel - if channel in self._channels: - # too early! we must have got an error - channel.waitclose() - # or else we raise one - raise IOError('connection unexpectedly closed: %s ' % ( - channel.gateway,)) + self._end_of_channel(channel) else: command, data = req if command == "links": - for link in self._links: - channel.send(link) - # completion marker, this host is done - channel.send(42) + self._process_link(channel) elif command == "done": - finishedcallback = self._channels.pop(channel) - if finishedcallback: - finishedcallback() + self._done(channel) elif command == "ack": if self._callback: self._callback("ack", self._paths[data], channel) elif command == "list_done": - # sum up all to send - if self._callback: - s = sum([self._paths[i] for i in self._to_send[channel]]) - self._callback("list", s, channel) + self._list_done(channel) elif command == "send": - modified_rel_path, checksum = data - modifiedpath = os.path.join(self._sourcedir, *modified_rel_path) - try: - f = open(modifiedpath, 'rb') - data = f.read() - except IOError: - data = None - - # provide info to progress callback function - modified_rel_path = "/".join(modified_rel_path) - if data is not None: - self._paths[modified_rel_path] = len(data) - else: - self._paths[modified_rel_path] = 0 - if channel not in self._to_send: - self._to_send[channel] = [] - self._to_send[channel].append(modified_rel_path) - - if data is not None: - f.close() - if checksum is not None and checksum == md5.md5(data).digest(): - data = None # not really modified - else: - # ! there is a reason for the interning: - # sharing multiple copies of the file's data - data = intern(data) - print '%s <= %s' % ( - channel.gateway.remoteaddress, - modified_rel_path) - channel.send(data) + self._send_item(channel, data) del data else: assert "Unknown command %s" % command @@ -124,6 +142,32 @@ def _send_link(self, basename, linkpoint): self._links.append(("link", basename, linkpoint)) + def _send_directory(self, path): + # dir: send a list of entries + names = [] + subpaths = [] + for name in os.listdir(path): + p = os.path.join(path, name) + if self.filter(p): + names.append(name) + subpaths.append(p) + self._broadcast(names) + for p in subpaths: + self._send_directory_structure(p) + + def _send_link_structure(self, path): + linkpoint = os.readlink(path) + basename = path[len(self._sourcedir) + 1:] + if not linkpoint.startswith(os.sep): + # relative link, just send it + # XXX: do sth with ../ links + self._send_link(basename, linkpoint) + elif linkpoint.startswith(self._sourcedir): + self._send_link(basename, linkpoint[len(self._sourcedir) + 1:]) + else: + self._send_link(basename, linkpoint) + self._broadcast(None) + def _send_directory_structure(self, path): try: st = os.lstat(path) @@ -134,29 +178,9 @@ # regular file: send a timestamp/size pair self._broadcast((st.st_mtime, st.st_size)) elif stat.S_ISDIR(st.st_mode): - # dir: send a list of entries - names = [] - subpaths = [] - for name in os.listdir(path): - p = os.path.join(path, name) - if self.filter(p): - names.append(name) - subpaths.append(p) - self._broadcast(names) - for p in subpaths: - self._send_directory_structure(p) + self._send_directory(path) elif stat.S_ISLNK(st.st_mode): - linkpoint = os.readlink(path) - basename = path[len(self._sourcedir) + 1:] - if not linkpoint.startswith(os.sep): - # relative link, just send it - # XXX: do sth with ../ links - self._send_link(basename, linkpoint) - elif linkpoint.startswith(self._sourcedir): - self._send_link(basename, linkpoint[len(self._sourcedir) + 1:]) - else: - self._send_link(basename, linkpoint) - self._broadcast(None) + self._send_link_structure(path) else: raise ValueError, "cannot sync %r" % (path,) From hpk at codespeak.net Sat Feb 3 12:18:26 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 3 Feb 2007 12:18:26 +0100 (CET) Subject: [py-svn] r37855 - py/trunk/py/doc Message-ID: <20070203111826.ABE5910061@code0.codespeak.net> Author: hpk Date: Sat Feb 3 12:18:25 2007 New Revision: 37855 Modified: py/trunk/py/doc/TODO.txt Log: a first rough list for py.test namespace cleanup Modified: py/trunk/py/doc/TODO.txt ============================================================================== --- py/trunk/py/doc/TODO.txt (original) +++ py/trunk/py/doc/TODO.txt Sat Feb 3 12:18:25 2007 @@ -78,6 +78,30 @@ with help code from py.__.rest.directive.... make sure that the txt files in py/documentation/ use it +* private py.test not-meant-to-be-public API: + here is a rough list what we want public on collectors: + + py.test.Collector. + startcapture() + finishcapture() + setup() + teardown() + listchain() + listnames() + run() + join() + * all collector class properties * + + and on py.test.Function|Item (which also has the collector interface): + execute() + + move all Outcome (Skipped/Passed/...) classes to + a global place (outcome.py?) + + all other attributes of collectors shall become private + +* after the above API cleanup there might be more :) + testing ----------- From hpk at codespeak.net Sat Feb 3 12:32:51 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 3 Feb 2007 12:32:51 +0100 (CET) Subject: [py-svn] r37859 - py/trunk/py/io/test Message-ID: <20070203113251.C102E10070@code0.codespeak.net> Author: hpk Date: Sat Feb 3 12:32:45 2007 New Revision: 37859 Added: py/trunk/py/io/test/test_fdcapture.py - copied, changed from r37854, py/trunk/py/io/test/test_capture.py py/trunk/py/io/test/test_stdcapture.py - copied, changed from r37854, py/trunk/py/io/test/test_capture.py Removed: py/trunk/py/io/test/test_capture.py Log: separating tests into two files Deleted: /py/trunk/py/io/test/test_capture.py ============================================================================== --- /py/trunk/py/io/test/test_capture.py Sat Feb 3 12:32:45 2007 +++ (empty file) @@ -1,186 +0,0 @@ -import os, sys -import py - -class TestFDCapture: - def test_basic(self): - tmpfile = py.std.os.tmpfile() - fd = tmpfile.fileno() - cap = py.io.FDCapture(fd) - os.write(fd, "hello") - f = cap.done() - s = f.read() - assert s == "hello" - - def test_stderr(self): - cap = py.io.FDCapture(2) - cap.setasfile('stderr') - print >>sys.stderr, "hello" - f = cap.done() - s = f.read() - assert s == "hello\n" - - def test_stdin(self): - f = os.tmpfile() - print >>f, "3" - f.seek(0) - cap = py.io.FDCapture(0, tmpfile=f) - # check with os.read() directly instead of raw_input(), because - # sys.stdin itself may be redirected (as py.test now does by default) - x = os.read(0, 100).strip() - f = cap.done() - assert x == "3" - - def test_writeorg(self): - tmppath = py.test.ensuretemp('test_writeorg').ensure('stderr', - file=True) - tmpfp = tmppath.open('w+b') - try: - cap = py.io.FDCapture(tmpfp.fileno()) - print >>tmpfp, 'foo' - cap.writeorg('bar\n') - finally: - tmpfp.close() - f = cap.done() - scap = f.read() - assert scap == 'foo\n' - stmp = tmppath.read() - assert stmp == "bar\n" - - def test_writeorg_wrongtype(self): - tmppath = py.test.ensuretemp('test_writeorg').ensure('stdout', - file=True) - tmpfp = tmppath.open('r') - try: - cap = py.io.FDCapture(tmpfp.fileno()) - py.test.raises(IOError, "cap.writeorg('bar\\n')") - finally: - tmpfp.close() - f = cap.done() - -class TestStdCapture: - def getcapture(self, **kw): - return py.io.StdCapture(**kw) - - def test_capturing_done_simple(self): - cap = self.getcapture() - print "hello world" - print >>sys.stderr, "hello error" - outfile, errfile = cap.done() - assert outfile.read() == "hello world\n" - assert errfile.read() == "hello error\n" - - def test_capturing_reset_simple(self): - cap = self.getcapture() - print "hello world" - print >>sys.stderr, "hello error" - out, err = cap.reset() - assert out == "hello world\n" - assert err == "hello error\n" - - def test_capturing_mixed(self): - cap = self.getcapture(mixed=True) - print "hello", - print >>sys.stderr, "world", - print >>sys.stdout, ".", - out, err = cap.reset() - assert out.strip() == "hello world ." - assert not err - - def test_capturing_twice_error(self): - cap = self.getcapture() - print "hello" - cap.reset() - py.test.raises(EnvironmentError, "cap.reset()") - - def test_capturing_modify_sysouterr_in_between(self): - oldout = sys.stdout - olderr = sys.stderr - cap = self.getcapture() - print "hello", - print >>sys.stderr, "world", - sys.stdout = py.std.StringIO.StringIO() - sys.stderr = py.std.StringIO.StringIO() - print "not seen" - print >>sys.stderr, "not seen" - out, err = cap.reset() - assert out == "hello" - assert err == "world" - assert sys.stdout == oldout - assert sys.stderr == olderr - - def test_capturing_error_recursive(self): - cap1 = self.getcapture() - print "cap1" - cap2 = self.getcapture() - print "cap2" - out2, err2 = cap2.reset() - py.test.raises(EnvironmentError, "cap2.reset()") - out1, err1 = cap1.reset() - assert out1 == "cap1\n" - assert out2 == "cap2\n" - - def test_just_out_capture(self): - cap = self.getcapture(out=True, err=False) - print >>sys.stdout, "hello" - print >>sys.stderr, "world" - out, err = cap.reset() - assert out == "hello\n" - assert not err - - def test_just_err_capture(self): - cap = self.getcapture(out=False, err=True) - print >>sys.stdout, "hello" - print >>sys.stderr, "world" - out, err = cap.reset() - assert err == "world\n" - assert not out - -class TestStdCaptureFD(TestStdCapture): - def getcapture(self, **kw): - return py.io.StdCaptureFD(**kw) - - def test_intermingling(self): - cap = self.getcapture() - os.write(1, "1") - print >>sys.stdout, 2, - os.write(1, "3") - os.write(2, "a") - print >>sys.stderr, "b", - os.write(2, "c") - out, err = cap.reset() - assert out == "123" - assert err == "abc" - - def test_callcapture(self): - def func(x, y): - print x - print >>py.std.sys.stderr, y - return 42 - - res, out, err = py.io.StdCaptureFD.call(func, 3, y=4) - assert res == 42 - assert out.startswith("3") - assert err.startswith("4") - -def test_capture_no_sys(): - cap = py.io.StdCaptureFD(patchsys=False) - print >>sys.stdout, "hello" - print >>sys.stderr, "world" - os.write(1, "1") - os.write(2, "2") - out, err = cap.reset() - assert out == "1" - assert err == "2" - -def test_callcapture_nofd(): - def func(x, y): - os.write(1, "hello") - os.write(2, "hello") - print x - print >>py.std.sys.stderr, y - return 42 - - res, out, err = py.io.StdCapture.call(func, 3, y=4) - assert res == 42 - assert out.startswith("3") - assert err.startswith("4") Copied: py/trunk/py/io/test/test_fdcapture.py (from r37854, py/trunk/py/io/test/test_capture.py) ============================================================================== --- py/trunk/py/io/test/test_capture.py (original) +++ py/trunk/py/io/test/test_fdcapture.py Sat Feb 3 12:32:45 2007 @@ -57,130 +57,3 @@ tmpfp.close() f = cap.done() -class TestStdCapture: - def getcapture(self, **kw): - return py.io.StdCapture(**kw) - - def test_capturing_done_simple(self): - cap = self.getcapture() - print "hello world" - print >>sys.stderr, "hello error" - outfile, errfile = cap.done() - assert outfile.read() == "hello world\n" - assert errfile.read() == "hello error\n" - - def test_capturing_reset_simple(self): - cap = self.getcapture() - print "hello world" - print >>sys.stderr, "hello error" - out, err = cap.reset() - assert out == "hello world\n" - assert err == "hello error\n" - - def test_capturing_mixed(self): - cap = self.getcapture(mixed=True) - print "hello", - print >>sys.stderr, "world", - print >>sys.stdout, ".", - out, err = cap.reset() - assert out.strip() == "hello world ." - assert not err - - def test_capturing_twice_error(self): - cap = self.getcapture() - print "hello" - cap.reset() - py.test.raises(EnvironmentError, "cap.reset()") - - def test_capturing_modify_sysouterr_in_between(self): - oldout = sys.stdout - olderr = sys.stderr - cap = self.getcapture() - print "hello", - print >>sys.stderr, "world", - sys.stdout = py.std.StringIO.StringIO() - sys.stderr = py.std.StringIO.StringIO() - print "not seen" - print >>sys.stderr, "not seen" - out, err = cap.reset() - assert out == "hello" - assert err == "world" - assert sys.stdout == oldout - assert sys.stderr == olderr - - def test_capturing_error_recursive(self): - cap1 = self.getcapture() - print "cap1" - cap2 = self.getcapture() - print "cap2" - out2, err2 = cap2.reset() - py.test.raises(EnvironmentError, "cap2.reset()") - out1, err1 = cap1.reset() - assert out1 == "cap1\n" - assert out2 == "cap2\n" - - def test_just_out_capture(self): - cap = self.getcapture(out=True, err=False) - print >>sys.stdout, "hello" - print >>sys.stderr, "world" - out, err = cap.reset() - assert out == "hello\n" - assert not err - - def test_just_err_capture(self): - cap = self.getcapture(out=False, err=True) - print >>sys.stdout, "hello" - print >>sys.stderr, "world" - out, err = cap.reset() - assert err == "world\n" - assert not out - -class TestStdCaptureFD(TestStdCapture): - def getcapture(self, **kw): - return py.io.StdCaptureFD(**kw) - - def test_intermingling(self): - cap = self.getcapture() - os.write(1, "1") - print >>sys.stdout, 2, - os.write(1, "3") - os.write(2, "a") - print >>sys.stderr, "b", - os.write(2, "c") - out, err = cap.reset() - assert out == "123" - assert err == "abc" - - def test_callcapture(self): - def func(x, y): - print x - print >>py.std.sys.stderr, y - return 42 - - res, out, err = py.io.StdCaptureFD.call(func, 3, y=4) - assert res == 42 - assert out.startswith("3") - assert err.startswith("4") - -def test_capture_no_sys(): - cap = py.io.StdCaptureFD(patchsys=False) - print >>sys.stdout, "hello" - print >>sys.stderr, "world" - os.write(1, "1") - os.write(2, "2") - out, err = cap.reset() - assert out == "1" - assert err == "2" - -def test_callcapture_nofd(): - def func(x, y): - os.write(1, "hello") - os.write(2, "hello") - print x - print >>py.std.sys.stderr, y - return 42 - - res, out, err = py.io.StdCapture.call(func, 3, y=4) - assert res == 42 - assert out.startswith("3") - assert err.startswith("4") Copied: py/trunk/py/io/test/test_stdcapture.py (from r37854, py/trunk/py/io/test/test_capture.py) ============================================================================== --- py/trunk/py/io/test/test_capture.py (original) +++ py/trunk/py/io/test/test_stdcapture.py Sat Feb 3 12:32:45 2007 @@ -1,62 +1,6 @@ import os, sys import py -class TestFDCapture: - def test_basic(self): - tmpfile = py.std.os.tmpfile() - fd = tmpfile.fileno() - cap = py.io.FDCapture(fd) - os.write(fd, "hello") - f = cap.done() - s = f.read() - assert s == "hello" - - def test_stderr(self): - cap = py.io.FDCapture(2) - cap.setasfile('stderr') - print >>sys.stderr, "hello" - f = cap.done() - s = f.read() - assert s == "hello\n" - - def test_stdin(self): - f = os.tmpfile() - print >>f, "3" - f.seek(0) - cap = py.io.FDCapture(0, tmpfile=f) - # check with os.read() directly instead of raw_input(), because - # sys.stdin itself may be redirected (as py.test now does by default) - x = os.read(0, 100).strip() - f = cap.done() - assert x == "3" - - def test_writeorg(self): - tmppath = py.test.ensuretemp('test_writeorg').ensure('stderr', - file=True) - tmpfp = tmppath.open('w+b') - try: - cap = py.io.FDCapture(tmpfp.fileno()) - print >>tmpfp, 'foo' - cap.writeorg('bar\n') - finally: - tmpfp.close() - f = cap.done() - scap = f.read() - assert scap == 'foo\n' - stmp = tmppath.read() - assert stmp == "bar\n" - - def test_writeorg_wrongtype(self): - tmppath = py.test.ensuretemp('test_writeorg').ensure('stdout', - file=True) - tmpfp = tmppath.open('r') - try: - cap = py.io.FDCapture(tmpfp.fileno()) - py.test.raises(IOError, "cap.writeorg('bar\\n')") - finally: - tmpfp.close() - f = cap.done() - class TestStdCapture: def getcapture(self, **kw): return py.io.StdCapture(**kw) From fijal at codespeak.net Sat Feb 3 13:13:19 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Sat, 3 Feb 2007 13:13:19 +0100 (CET) Subject: [py-svn] r37861 - py/trunk/py/misc/testing Message-ID: <20070203121319.EB6931006E@code0.codespeak.net> Author: fijal Date: Sat Feb 3 13:13:18 2007 New Revision: 37861 Modified: py/trunk/py/misc/testing/test_initpkg.py Log: Skip compat Modified: py/trunk/py/misc/testing/test_initpkg.py ============================================================================== --- py/trunk/py/misc/testing/test_initpkg.py (original) +++ py/trunk/py/misc/testing/test_initpkg.py Sat Feb 3 13:13:18 2007 @@ -61,6 +61,7 @@ base.join('magic', 'greenlet.py'), base.join('bin'), base.join('execnet', 'script'), + base.join('compat', 'testing'), ) for p in base.visit('*.py', lambda x: x.check(dotfile=0)): if p.basename == '__init__.py': From fijal at codespeak.net Sat Feb 3 13:14:50 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Sat, 3 Feb 2007 13:14:50 +0100 (CET) Subject: [py-svn] r37862 - in py/trunk/py: . doc doc/example/pytest test test/rsession test/terminal test/testing Message-ID: <20070203121450.455F91006E@code0.codespeak.net> Author: fijal Date: Sat Feb 3 13:14:46 2007 New Revision: 37862 Added: py/trunk/py/test/outcome.py Modified: py/trunk/py/__init__.py py/trunk/py/doc/example/pytest/test_failures.py py/trunk/py/doc/test_conftest.py py/trunk/py/test/collect.py py/trunk/py/test/item.py py/trunk/py/test/raises.py py/trunk/py/test/rsession/executor.py py/trunk/py/test/rsession/rsession.py py/trunk/py/test/session.py py/trunk/py/test/terminal/remote.py py/trunk/py/test/terminal/terminal.py py/trunk/py/test/testing/test_collect.py py/trunk/py/test/testing/test_doctest.py py/trunk/py/test/testing/test_session.py Log: Move Skipped/Failed/Passed out of public namespace Modified: py/trunk/py/__init__.py ============================================================================== --- py/trunk/py/__init__.py (original) +++ py/trunk/py/__init__.py Sat Feb 3 13:14:46 2007 @@ -27,6 +27,7 @@ 'test.skip' : ('./test/item.py', 'skip'), 'test.fail' : ('./test/item.py', 'fail'), 'test.exit' : ('./test/session.py', 'exit'), + 'test.pdb' : ('./test/pdbplus.py', 'start_pdb'), # configuration/initialization related test api 'test.config' : ('./test/config.py', 'config_per_process'), Modified: py/trunk/py/doc/example/pytest/test_failures.py ============================================================================== --- py/trunk/py/doc/example/pytest/test_failures.py (original) +++ py/trunk/py/doc/example/pytest/test_failures.py Sat Feb 3 13:14:46 2007 @@ -1,12 +1,13 @@ import py failure_demo = py.magic.autopath().dirpath('failure_demo.py') +from py.__.test.outcome import Failed, Passed def test_failure_demo_fails_properly(): config = py.test.config._reparse([failure_demo]) session = config.initsession() session.main() - l = session.getitemoutcomepairs(py.test.Item.Failed) + l = session.getitemoutcomepairs(Failed) assert len(l) == 21 - l = session.getitemoutcomepairs(py.test.Item.Passed) + l = session.getitemoutcomepairs(Passed) assert not l Modified: py/trunk/py/doc/test_conftest.py ============================================================================== --- py/trunk/py/doc/test_conftest.py (original) +++ py/trunk/py/doc/test_conftest.py Sat Feb 3 13:14:46 2007 @@ -1,5 +1,6 @@ import py +from py.__.test.outcome import Skipped, Failed, Passed def setup_module(mod): mod.tmpdir = py.test.ensuretemp('docdoctest') @@ -30,10 +31,10 @@ config = py.test.config._reparse([xtxt]) session = config.initsession() session.main() - l = session.getitemoutcomepairs(py.test.Item.Failed) + l = session.getitemoutcomepairs(Failed) assert len(l) == 0 - l = session.getitemoutcomepairs(py.test.Item.Passed) - l2 = session.getitemoutcomepairs(py.test.Item.Skipped) + l = session.getitemoutcomepairs(Passed) + l2 = session.getitemoutcomepairs(Skipped) assert len(l+l2) == 2 def test_doctest_eol(): @@ -45,10 +46,10 @@ config = py.test.config._reparse([ytxt]) session = config.initsession() session.main() - l = session.getitemoutcomepairs(py.test.Item.Failed) + l = session.getitemoutcomepairs(Failed) assert len(l) == 0 - l = session.getitemoutcomepairs(py.test.Item.Passed) - l2 = session.getitemoutcomepairs(py.test.Item.Skipped) + l = session.getitemoutcomepairs(Passed) + l2 = session.getitemoutcomepairs(Skipped) assert len(l+l2) == 2 def test_js_ignore(): @@ -63,10 +64,10 @@ config = py.test.config._reparse([xtxt]) session = config.initsession() session.main() - l = session.getitemoutcomepairs(py.test.Item.Failed) + l = session.getitemoutcomepairs(Failed) assert len(l) == 0 - l = session.getitemoutcomepairs(py.test.Item.Passed) - l2 = session.getitemoutcomepairs(py.test.Item.Skipped) + l = session.getitemoutcomepairs(Passed) + l2 = session.getitemoutcomepairs(Skipped) assert len(l+l2) == 3 def test_resolve_linkrole(): Modified: py/trunk/py/test/collect.py ============================================================================== --- py/trunk/py/test/collect.py (original) +++ py/trunk/py/test/collect.py Sat Feb 3 13:14:46 2007 @@ -56,27 +56,6 @@ Generator = configproperty('Generator') _stickyfailure = None - class Outcome: - def __init__(self, msg=None, excinfo=None): - self.msg = msg - self.excinfo = excinfo - def __repr__(self): - if self.msg: - return self.msg - return "<%s instance>" %(self.__class__.__name__,) - __str__ = __repr__ - - class Passed(Outcome): - pass - class Failed(Outcome): - pass - class ExceptionFailure(Failed): - def __init__(self, expr, expected, msg=None, excinfo=None): - Collector.Failed.__init__(self, msg=msg, excinfo=excinfo) - self.expr = expr - self.expected = expected - class Skipped(Outcome): - pass def __repr__(self): return "<%s %r>" %(self.__class__.__name__, self.name) Modified: py/trunk/py/test/item.py ============================================================================== --- py/trunk/py/test/item.py (original) +++ py/trunk/py/test/item.py Sat Feb 3 13:14:46 2007 @@ -1,6 +1,7 @@ import py from inspect import isclass, ismodule +from py.__.test.outcome import Skipped, Failed, Passed _dummy = object() @@ -101,10 +102,10 @@ def skip(msg="unknown reason"): """ skip with the given Message. """ __tracebackhide__ = True - raise py.test.Item.Skipped(msg=msg) + raise Skipped(msg=msg) def fail(msg="unknown failure"): """ fail with the given Message. """ __tracebackhide__ = True - raise py.test.Item.Failed(msg=msg) + raise Failed(msg=msg) Added: py/trunk/py/test/outcome.py ============================================================================== --- (empty file) +++ py/trunk/py/test/outcome.py Sat Feb 3 13:14:46 2007 @@ -0,0 +1,29 @@ + +""" File defining possible outcomes of running +""" + +class Outcome: + def __init__(self, msg=None, excinfo=None): + self.msg = msg + self.excinfo = excinfo + + def __repr__(self): + if self.msg: + return self.msg + return "<%s instance>" %(self.__class__.__name__,) + __str__ = __repr__ + +class Passed(Outcome): + pass + +class Failed(Outcome): + pass + +class ExceptionFailure(Failed): + def __init__(self, expr, expected, msg=None, excinfo=None): + Failed.__init__(self, msg=msg, excinfo=excinfo) + self.expr = expr + self.expected = expected + +class Skipped(Outcome): + pass Modified: py/trunk/py/test/raises.py ============================================================================== --- py/trunk/py/test/raises.py (original) +++ py/trunk/py/test/raises.py Sat Feb 3 13:14:46 2007 @@ -1,6 +1,6 @@ import sys import py -ExceptionFailure = py.test.Item.ExceptionFailure +from py.__.test.outcome import ExceptionFailure def raises(ExpectedException, *args, **kwargs): """ raise AssertionError, if target code does not raise the expected Modified: py/trunk/py/test/rsession/executor.py ============================================================================== --- py/trunk/py/test/rsession/executor.py (original) +++ py/trunk/py/test/rsession/executor.py Sat Feb 3 13:14:46 2007 @@ -6,6 +6,7 @@ from py.__.test.rsession.outcome import Outcome, ReprOutcome from py.__.test.rsession.box import Box from py.__.test.rsession import report +from py.__.test.outcome import Skipped, Failed class RunExecutor(object): """ Same as in executor, but just running run @@ -26,7 +27,7 @@ try: self.run() outcome = Outcome() - except py.test.Item.Skipped, e: + except Skipped, e: outcome = Outcome(skipped=str(e)) except (KeyboardInterrupt, SystemExit): raise Modified: py/trunk/py/test/rsession/rsession.py ============================================================================== --- py/trunk/py/test/rsession/rsession.py (original) +++ py/trunk/py/test/rsession/rsession.py Sat Feb 3 13:14:46 2007 @@ -17,6 +17,7 @@ box_runner from py.__.test.rsession.reporter import LocalReporter, RemoteReporter from py.__.test.session import Session +from py.__.test.outcome import Skipped, Failed class AbstractSession(Session): """ @@ -84,7 +85,7 @@ excinfo, item = data if excinfo is None: reporter(report.ItemStart(item)) - elif excinfo.type is py.test.Item.Skipped: + elif excinfo.type is Skipped: reporter(report.SkippedTryiter(excinfo, item)) else: reporter(report.FailedTryiter(excinfo, item)) Modified: py/trunk/py/test/session.py ============================================================================== --- py/trunk/py/test/session.py (original) +++ py/trunk/py/test/session.py Sat Feb 3 13:14:46 2007 @@ -1,4 +1,5 @@ import py +from py.__.test.outcome import Outcome, Failed, Passed, Skipped class Session(object): """ @@ -82,17 +83,17 @@ outcome = self.run(colitem) except (KeyboardInterrupt, Exit): raise - except colitem.Outcome, outcome: + except Outcome, outcome: if outcome.excinfo is None: outcome.excinfo = py.code.ExceptionInfo() except: excinfo = py.code.ExceptionInfo() - outcome = colitem.Failed(excinfo=excinfo) + outcome = Failed(excinfo=excinfo) assert (outcome is None or - isinstance(outcome, (list, colitem.Outcome))) + isinstance(outcome, (list, Outcome))) finally: self.finish(colitem, outcome) - if isinstance(outcome, colitem.Failed) and self.config.option.exitfirst: + if isinstance(outcome, Failed) and self.config.option.exitfirst: py.test.exit("exit on first problem configured.", item=colitem) finally: colitem.finishcapture() @@ -104,7 +105,7 @@ colitem.skipbykeyword(self.config.option.keyword) res = colitem.run() if res is None: - return py.test.Item.Passed() + return Passed() elif not isinstance(res, (list, tuple)): raise TypeError("%r.run() returned neither " "list, tuple nor None: %r" % (colitem, res)) Modified: py/trunk/py/test/terminal/remote.py ============================================================================== --- py/trunk/py/test/terminal/remote.py (original) +++ py/trunk/py/test/terminal/remote.py Sat Feb 3 13:14:46 2007 @@ -1,7 +1,8 @@ from __future__ import generators import py from py.__.test.session import Session -from py.__.test.terminal.out import getout +from py.__.test.terminal.out import getout +from py.__.test.outcome import Failed, Passed, Skipped def checkpyfilechange(rootdir, statcache={}): """ wait until project files are changed. """ @@ -135,6 +136,6 @@ session.shouldclose = channel.isclosed print "SLAVE: starting session.main()" session.main() - failures = session.getitemoutcomepairs(py.test.Item.Failed) + failures = session.getitemoutcomepairs(Failed) failures = [config.get_collector_trail(item) for item,_ in failures] channel.send(failures) Modified: py/trunk/py/test/terminal/terminal.py ============================================================================== --- py/trunk/py/test/terminal/terminal.py (original) +++ py/trunk/py/test/terminal/terminal.py Sat Feb 3 13:14:46 2007 @@ -4,6 +4,7 @@ Item = py.test.Item from py.__.test.terminal.out import getout from py.__.test.representation import Presenter +from py.__.test.outcome import Skipped, Passed, Failed def getrelpath(source, dest): base = source.common(dest) @@ -167,14 +168,14 @@ # progress information # -------------------- typemap = { - Item.Passed: '.', - Item.Skipped: 's', - Item.Failed: 'F', + Passed: '.', + Skipped: 's', + Failed: 'F', } namemap = { - Item.Passed: 'ok', - Item.Skipped: 'SKIP', - Item.Failed: 'FAIL', + Passed: 'ok', + Skipped: 'SKIP', + Failed: 'FAIL', } def repr_progress_short_result(self, item, outcome): @@ -194,11 +195,11 @@ return 'UNKNOWN' def repr_progress_module_result(self, item, outcome): - if isinstance(outcome, py.test.Item.Failed): + if isinstance(outcome, Failed): return "FAILED TO LOAD MODULE" - elif isinstance(outcome, py.test.Item.Skipped): + elif isinstance(outcome, Skipped): return "skipped" - elif not isinstance(outcome, (list, py.test.Item.Passed)): + elif not isinstance(outcome, (list, Passed)): return "?" # -------------------- @@ -207,7 +208,7 @@ def summaryline(self): outlist = [] sum = 0 - for typ in Item.Passed, Item.Failed, Item.Skipped: + for typ in Passed, Failed, Skipped: l = self.getitemoutcomepairs(typ) if l: outlist.append('%d %s' % (len(l), typ.__name__.lower())) @@ -232,7 +233,7 @@ def skippedreasons(self): texts = {} - for colitem, outcome in self.getitemoutcomepairs(Item.Skipped): + for colitem, outcome in self.getitemoutcomepairs(Skipped): raisingtb = self.getlastvisible(outcome.excinfo.traceback) fn = raisingtb.frame.code.path lineno = raisingtb.lineno @@ -251,7 +252,7 @@ def failures(self): if self.config.option.tbstyle == 'no': return # skip the detailed failure reports altogether - l = self.getitemoutcomepairs(Item.Failed) + l = self.getitemoutcomepairs(Failed) if l: self.out.sep('_') for colitem, outcome in l: Modified: py/trunk/py/test/testing/test_collect.py ============================================================================== --- py/trunk/py/test/testing/test_collect.py (original) +++ py/trunk/py/test/testing/test_collect.py Sat Feb 3 13:14:46 2007 @@ -1,6 +1,7 @@ from __future__ import generators import py from setupdata import setupdatadir +from py.__.test.outcome import Skipped, Failed, Passed, Outcome def setup_module(mod): mod.datadir = setupdatadir() @@ -205,7 +206,7 @@ out = py.std.cStringIO.StringIO() session = config._getsessionclass()(config, out) session.main() - l = session.getitemoutcomepairs(py.test.Item.Passed) + l = session.getitemoutcomepairs(Passed) assert len(l) == 2 finally: old.chdir() @@ -215,7 +216,7 @@ out = py.std.cStringIO.StringIO() session = config._getsessionclass()(config, out) session.main() - l = session.getitemoutcomepairs(py.test.Item.Passed) + l = session.getitemoutcomepairs(Passed) assert len(l) == 2 def test_custom_NONpython_collection_from_conftest(): @@ -252,7 +253,7 @@ out = py.std.cStringIO.StringIO() session = config._getsessionclass()(config, out) session.main() - l = session.getitemoutcomepairs(py.test.Item.Passed) + l = session.getitemoutcomepairs(Passed) assert len(l) == 1 finally: old.chdir() @@ -262,7 +263,7 @@ out = py.std.cStringIO.StringIO() session = config._getsessionclass()(config, out) session.main() - l = session.getitemoutcomepairs(py.test.Item.Passed) + l = session.getitemoutcomepairs(Passed) assert len(l) == 1 def test_order_of_execution_generator_same_codeline(): Modified: py/trunk/py/test/testing/test_doctest.py ============================================================================== --- py/trunk/py/test/testing/test_doctest.py (original) +++ py/trunk/py/test/testing/test_doctest.py Sat Feb 3 13:14:46 2007 @@ -1,6 +1,7 @@ import py from py.__.test.doctest import DoctestText +from py.__.test.outcome import Skipped, Failed, Passed, Outcome def test_simple_docteststring(): testitem = DoctestText(name="dummy", parent=None) @@ -19,7 +20,7 @@ >>> i + 1 2 """) - py.test.raises(py.test.Item.Failed, "testitem.run()") + py.test.raises(Failed, "testitem.run()") def test_collect_doctest_files_with_test_prefix(): Modified: py/trunk/py/test/testing/test_session.py ============================================================================== --- py/trunk/py/test/testing/test_session.py (original) +++ py/trunk/py/test/testing/test_session.py Sat Feb 3 13:14:46 2007 @@ -1,5 +1,6 @@ import py from setupdata import setup_module # sets up global 'tmpdir' +from py.__.test.outcome import Skipped, Failed, Passed, Outcome implied_options = { '--pdb': 'usepdb and nocapture', @@ -44,9 +45,9 @@ config = py.test.config._reparse(opts + [datadir/'filetest.py']) session = config.initsession() session.main() - l = session.getitemoutcomepairs(py.test.Item.Failed) + l = session.getitemoutcomepairs(Failed) assert len(l) == 2 - l = session.getitemoutcomepairs(py.test.Item.Passed) + l = session.getitemoutcomepairs(Passed) assert not l class TestKeywordSelection: @@ -56,11 +57,11 @@ '-k', keyword]) session = config._getsessionclass()(config, py.std.sys.stdout) session.main() - l = session.getitemoutcomepairs(py.test.Item.Failed) + l = session.getitemoutcomepairs(Failed) assert len(l) == 1 item = l[0][0] assert item.name == 'test_one' - l = session.getitemoutcomepairs(py.test.Item.Skipped) + l = session.getitemoutcomepairs(Skipped) assert len(l) == 1 def test_select_extra_keywords(self): @@ -86,10 +87,10 @@ session = config._getsessionclass()(config, f) session.main() print "keyword", repr(keyword) - l = session.getitemoutcomepairs(py.test.Item.Passed) + l = session.getitemoutcomepairs(Passed) assert len(l) == 1 assert l[0][0].name == 'test_2' - l = session.getitemoutcomepairs(py.test.Item.Skipped) + l = session.getitemoutcomepairs(Skipped) assert l[0][0].name == 'test_1' class TestTerminalSession: @@ -104,13 +105,13 @@ def test_terminal(self): session = self.mainsession(datadir / 'filetest.py') out = self.file.getvalue() - l = session.getitemoutcomepairs(py.test.Item.Failed) + l = session.getitemoutcomepairs(Failed) assert len(l) == 2 assert out.find('2 failed') != -1 def test_syntax_error_module(self): session = self.mainsession(datadir / 'syntax_error.py') - l = session.getitemoutcomepairs(py.test.Item.Failed) + l = session.getitemoutcomepairs(Failed) assert len(l) == 1 out = self.file.getvalue() assert out.find(str('syntax_error.py')) != -1 @@ -120,9 +121,9 @@ session = self.mainsession("--exitfirst", datadir / 'filetest.py') assert session.config.option.exitfirst - l = session.getitemoutcomepairs(py.test.Item.Failed) + l = session.getitemoutcomepairs(Failed) assert len(l) == 1 - l = session.getitemoutcomepairs(py.test.Item.Passed) + l = session.getitemoutcomepairs(Passed) assert not l def test_collectonly(self): @@ -131,7 +132,7 @@ assert session.config.option.collectonly out = self.file.getvalue() #print out - l = session.getitemoutcomepairs(py.test.Item.Failed) + l = session.getitemoutcomepairs(Failed) #if l: # x = l[0][1].excinfo # print x.exconly() @@ -195,7 +196,7 @@ self._testmycapture = self._mycapture.reset() """)) session = self.mainsession(o) - l = session.getitemoutcomepairs(py.test.Item.Passed) + l = session.getitemoutcomepairs(Passed) assert len(l) == 1 item = l[0][0] assert hasattr(item, '_testmycapture') @@ -250,9 +251,9 @@ """)) session = self.mainsession(o) - l = session.getitemoutcomepairs(py.test.Item.Failed) + l = session.getitemoutcomepairs(Failed) assert len(l) == 0 - l = session.getitemoutcomepairs(py.test.Item.Passed) + l = session.getitemoutcomepairs(Passed) assert len(l) == 7 # also test listnames() here ... item, result = l[-1] @@ -273,7 +274,7 @@ a = 1 """)) session = self.mainsession(o) - l = session.getitemoutcomepairs(py.test.Item.Failed) + l = session.getitemoutcomepairs(Failed) assert len(l) == 1 item, outcome = l[0] assert str(outcome.excinfo).find('does_not_work') != -1 @@ -284,7 +285,7 @@ print 'Output of simulated "py.test brokenrepr.py":' print out - l = session.getitemoutcomepairs(py.test.Item.Failed) + l = session.getitemoutcomepairs(Failed) assert len(l) == 2 assert out.find("""[Exception("Ha Ha fooled you, I'm a broken repr().") raised in repr()]""") != -1 #' assert out.find("[unknown exception raised in repr()]") != -1 From fijal at codespeak.net Sat Feb 3 13:15:24 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Sat, 3 Feb 2007 13:15:24 +0100 (CET) Subject: [py-svn] r37863 - py/trunk/py Message-ID: <20070203121524.F07001006F@code0.codespeak.net> Author: fijal Date: Sat Feb 3 13:15:23 2007 New Revision: 37863 Modified: py/trunk/py/__init__.py Log: Ooops, didn't meant to check that in. Modified: py/trunk/py/__init__.py ============================================================================== --- py/trunk/py/__init__.py (original) +++ py/trunk/py/__init__.py Sat Feb 3 13:15:23 2007 @@ -27,7 +27,6 @@ 'test.skip' : ('./test/item.py', 'skip'), 'test.fail' : ('./test/item.py', 'fail'), 'test.exit' : ('./test/session.py', 'exit'), - 'test.pdb' : ('./test/pdbplus.py', 'start_pdb'), # configuration/initialization related test api 'test.config' : ('./test/config.py', 'config_per_process'), From fijal at codespeak.net Sat Feb 3 13:25:21 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Sat, 3 Feb 2007 13:25:21 +0100 (CET) Subject: [py-svn] r37864 - py/trunk/py/misc/testing Message-ID: <20070203122521.6B3AC10061@code0.codespeak.net> Author: fijal Date: Sat Feb 3 13:25:20 2007 New Revision: 37864 Modified: py/trunk/py/misc/testing/test_initpkg.py Log: Add a (failing) test about version URL available Modified: py/trunk/py/misc/testing/test_initpkg.py ============================================================================== --- py/trunk/py/misc/testing/test_initpkg.py (original) +++ py/trunk/py/misc/testing/test_initpkg.py Sat Feb 3 13:25:20 2007 @@ -239,3 +239,9 @@ ##def test_help(): # help(std.path) # #assert False + +def test_url_of_version(): + py.test.skip("FAILING!") + from urllib import URLopener + URLopener().open(py.__package__.download_url) + From fijal at codespeak.net Sat Feb 3 13:27:52 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Sat, 3 Feb 2007 13:27:52 +0100 (CET) Subject: [py-svn] r37865 - py/trunk/py/misc/testing Message-ID: <20070203122752.1535B1006E@code0.codespeak.net> Author: fijal Date: Sat Feb 3 13:27:48 2007 New Revision: 37865 Modified: py/trunk/py/misc/testing/test_initpkg.py Log: more descriptive skip msg Modified: py/trunk/py/misc/testing/test_initpkg.py ============================================================================== --- py/trunk/py/misc/testing/test_initpkg.py (original) +++ py/trunk/py/misc/testing/test_initpkg.py Sat Feb 3 13:27:48 2007 @@ -241,7 +241,7 @@ # #assert False def test_url_of_version(): - py.test.skip("FAILING!") + py.test.skip("FAILING! - provide a proper URL or upload pylib tgz") from urllib import URLopener URLopener().open(py.__package__.download_url) From hpk at codespeak.net Sat Feb 3 13:31:54 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 3 Feb 2007 13:31:54 +0100 (CET) Subject: [py-svn] r37866 - py/trunk/py/execnet Message-ID: <20070203123154.95AC210061@code0.codespeak.net> Author: hpk Date: Sat Feb 3 13:31:47 2007 New Revision: 37866 Modified: py/trunk/py/execnet/register.py Log: small cleanup of ground std* descriptors. Modified: py/trunk/py/execnet/register.py ============================================================================== --- py/trunk/py/execnet/register.py (original) +++ py/trunk/py/execnet/register.py Sat Feb 3 13:31:47 2007 @@ -171,16 +171,19 @@ devnull = 'NUL' else: devnull = '/dev/null' + # stdin sys.stdin = os.fdopen(os.dup(0), 'rb', 0) - sys.stdout = os.fdopen(os.dup(1), 'wb', 0) - if os.name == 'nt': - sys.stderr = os.fdopen(os.dup(2), 'wb', 0) fd = os.open(devnull, os.O_RDONLY) os.dup2(fd, 0) os.close(fd) + + # stdout + sys.stdout = os.fdopen(os.dup(1), 'wb', 0) fd = os.open(devnull, os.O_WRONLY) os.dup2(fd, 1) + + # stderr for win32 if os.name == 'nt': + sys.stderr = os.fdopen(os.dup(2), 'wb', 0) os.dup2(fd, 2) os.close(fd) - From hpk at codespeak.net Sat Feb 3 14:57:27 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 3 Feb 2007 14:57:27 +0100 (CET) Subject: [py-svn] r37872 - in py/trunk/py/io: . test Message-ID: <20070203135727.146FC10072@code0.codespeak.net> Author: hpk Date: Sat Feb 3 14:57:25 2007 New Revision: 37872 Modified: py/trunk/py/io/stdcapture.py py/trunk/py/io/test/test_stdcapture.py Log: StdCaptureFD and StdCapture now try to take care of stdin in a mostly uniform way. Modified: py/trunk/py/io/stdcapture.py ============================================================================== --- py/trunk/py/io/stdcapture.py (original) +++ py/trunk/py/io/stdcapture.py Sat Feb 3 14:57:25 2007 @@ -4,7 +4,6 @@ try: from cStringIO import StringIO except ImportError: from StringIO import StringIO -emptyfile = StringIO() class Capture(object): def call(cls, func, *args, **kwargs): @@ -33,10 +32,17 @@ class StdCaptureFD(Capture): - """ capture Stdout and Stderr both on filedescriptor - and sys.stdout/stderr level. + """ This class allows to capture writes to FD1 and FD2 + and may connect a NULL file to FD0 (and prevent + reads from sys.stdin) """ - def __init__(self, out=True, err=True, mixed=False, patchsys=True): + def __init__(self, out=True, err=True, mixed=False, in_=True, patchsys=True): + if in_: + self._oldin = (sys.stdin, os.dup(0)) + sys.stdin = DontReadFromInput() + fd = os.open(devnullpath, os.O_RDONLY) + os.dup2(fd, 0) + os.close(fd) if out: self.out = py.io.FDCapture(1) if patchsys: @@ -57,17 +63,23 @@ outfile = self.out.done() if hasattr(self, 'err'): errfile = self.err.done() + if hasattr(self, '_oldin'): + oldsys, oldfd = self._oldin + os.dup2(oldfd, 0) + os.close(oldfd) + sys.stdin = oldsys return outfile, errfile class StdCapture(Capture): - """ capture sys.stdout/sys.stderr (but not system level fd 1 and 2). - - This class allows to capture writes to sys.stdout|stderr "in-memory" - and will raise errors on tries to read from sys.stdin. + """ This class allows to capture writes to sys.stdout|stderr "in-memory" + and will raise errors on tries to read from sys.stdin. It only + modifies sys.stdout|stderr|stdin attributes and does not + touch underlying File Descriptors (use StdCaptureFD for that). """ - def __init__(self, out=True, err=True, mixed=False): + def __init__(self, out=True, err=True, in_=True, mixed=False): self._out = out self._err = err + self._in = in_ if out: self.oldout = sys.stdout sys.stdout = self.newout = StringIO() @@ -78,8 +90,9 @@ else: newerr = StringIO() sys.stderr = self.newerr = newerr - self.oldin = sys.stdin - sys.stdin = self.newin = DontReadFromInput() + if in_: + self.oldin = sys.stdin + sys.stdin = self.newin = DontReadFromInput() def reset(self): """ return captured output as strings and restore sys.stdout/err.""" @@ -106,7 +119,8 @@ del self.olderr errfile = self.newerr errfile.seek(0) - sys.stdin = self.oldin + if self._in: + sys.stdin = self.oldin return outfile, errfile class DontReadFromInput: @@ -121,3 +135,14 @@ readline = read readlines = read __iter__ = read + +try: + devnullpath = os.devnull +except AttributeError: + if os.name == 'nt': + devnullpath = 'NUL' + else: + devnullpath = '/dev/null' + +emptyfile = StringIO() + Modified: py/trunk/py/io/test/test_stdcapture.py ============================================================================== --- py/trunk/py/io/test/test_stdcapture.py (original) +++ py/trunk/py/io/test/test_stdcapture.py Sat Feb 3 14:57:25 2007 @@ -79,6 +79,22 @@ assert err == "world\n" assert not out + def test_stdin_restored(self): + old = sys.stdin + cap = self.getcapture(in_=True) + newstdin = sys.stdin + out, err = cap.reset() + assert newstdin != sys.stdin + assert sys.stdin is old + + def test_stdin_nulled_by_default(self): + print "XXX this test may well hang instead of crashing" + print "XXX which indicates an error in the underlying capturing" + print "XXX mechanisms" + cap = self.getcapture() + py.test.raises(IOError, "sys.stdin.read()") + out, err = cap.reset() + class TestStdCaptureFD(TestStdCapture): def getcapture(self, **kw): return py.io.StdCaptureFD(**kw) From fijal at codespeak.net Sat Feb 3 19:00:08 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Sat, 3 Feb 2007 19:00:08 +0100 (CET) Subject: [py-svn] r37876 - in py/trunk/py: doc test test/rsession test/rsession/testing Message-ID: <20070203180008.60CF210071@code0.codespeak.net> Author: fijal Date: Sat Feb 3 19:00:04 2007 New Revision: 37876 Modified: py/trunk/py/doc/TODO.txt py/trunk/py/test/collect.py py/trunk/py/test/rsession/master.py py/trunk/py/test/rsession/testing/test_master.py py/trunk/py/test/rsession/testing/test_slave.py Log: s/get_collector_trail/_get_collector_trail/ Modified: py/trunk/py/doc/TODO.txt ============================================================================== --- py/trunk/py/doc/TODO.txt (original) +++ py/trunk/py/doc/TODO.txt Sat Feb 3 19:00:04 2007 @@ -81,7 +81,7 @@ * private py.test not-meant-to-be-public API: here is a rough list what we want public on collectors: - py.test.Collector. + py.test.collect.Collector. startcapture() finishcapture() setup() Modified: py/trunk/py/test/collect.py ============================================================================== --- py/trunk/py/test/collect.py (original) +++ py/trunk/py/test/collect.py Sat Feb 3 19:00:04 2007 @@ -231,7 +231,7 @@ def getouterr(self): return self.captured_out, self.captured_err - def get_collector_trail(self): + def _get_collector_trail(self): """ Shortcut """ return self.config.get_collector_trail(self) Modified: py/trunk/py/test/rsession/master.py ============================================================================== --- py/trunk/py/test/rsession/master.py (original) +++ py/trunk/py/test/rsession/master.py Sat Feb 3 19:00:04 2007 @@ -30,7 +30,7 @@ else: self.pending.insert(0, item) #itemspec = item.listnames()[1:] - self.channel.send(item.get_collector_trail()) + self.channel.send(item._get_collector_trail()) # send start report self.reporter(report.SendItem(self.channel, item)) Modified: py/trunk/py/test/rsession/testing/test_master.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_master.py (original) +++ py/trunk/py/test/rsession/testing/test_master.py Sat Feb 3 19:00:04 2007 @@ -39,7 +39,7 @@ self.sent.append(item) class Item(py.test.Item): - def get_collector_trail(self): + def _get_collector_trail(self): return (self.name,) def test_masternode(): @@ -96,7 +96,7 @@ gw = py.execnet.PopenGateway() config = py.test.config._reparse([]) channel = setup_slave(gw, pkgdir, config) - spec = rootcol.getitembynames(funcpass_spec).get_collector_trail() + spec = rootcol.getitembynames(funcpass_spec)._get_collector_trail() channel.send(spec) output = ReprOutcome(channel.receive()) assert output.passed Modified: py/trunk/py/test/rsession/testing/test_slave.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_slave.py (original) +++ py/trunk/py/test/rsession/testing/test_slave.py Sat Feb 3 19:00:04 2007 @@ -62,7 +62,7 @@ def test_slave_run_passing(): node = gettestnode() item = rootcol.getitembynames(funcpass_spec) - outcome = node.execute(item.get_collector_trail()) + outcome = node.execute(item._get_collector_trail()) assert outcome.passed assert not outcome.setupfailure @@ -74,7 +74,7 @@ def test_slave_run_failing(): node = gettestnode() item = rootcol.getitembynames(funcfail_spec) - outcome = node.execute(item.get_collector_trail()) + outcome = node.execute(item._get_collector_trail()) assert not outcome.passed assert not outcome.setupfailure assert len(outcome.excinfo.traceback) == 1 @@ -89,7 +89,7 @@ def test_slave_run_skipping(): node = gettestnode() item = rootcol.getitembynames(funcskip_spec) - outcome = node.execute(item.get_collector_trail()) + outcome = node.execute(item._get_collector_trail()) assert not outcome.passed assert outcome.skipped @@ -101,7 +101,7 @@ def test_slave_run_failing_wrapped(): node = gettestnode() item = rootcol.getitembynames(funcfail_spec) - repr_outcome = node.run(item.get_collector_trail()) + repr_outcome = node.run(item._get_collector_trail()) outcome = ReprOutcome(repr_outcome) assert not outcome.passed assert not outcome.setupfailure @@ -112,8 +112,8 @@ failitem = rootcol.getitembynames(funcfail_spec) passitem = rootcol.getitembynames(funcpass_spec) q = [None, - passitem.get_collector_trail(), - failitem.get_collector_trail() + passitem._get_collector_trail(), + failitem._get_collector_trail() ] config = py.test.config._reparse([]) pidinfo = PidInfo() @@ -125,7 +125,7 @@ def test_slave_run_different_stuff(): node = gettestnode() node.run(rootcol.getitembynames("py doc log.txt".split()). - get_collector_trail()) + _get_collector_trail()) def test_slave_setup_exit(): tmp = py.test.ensuretemp("slaveexit") From fijal at codespeak.net Sat Feb 3 19:29:29 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Sat, 3 Feb 2007 19:29:29 +0100 (CET) Subject: [py-svn] r37877 - in py/trunk/py/test: . rsession rsession/testing terminal testing Message-ID: <20070203182929.6031810071@code0.codespeak.net> Author: fijal Date: Sat Feb 3 19:29:24 2007 New Revision: 37877 Modified: py/trunk/py/test/collect.py py/trunk/py/test/config.py py/trunk/py/test/item.py py/trunk/py/test/representation.py py/trunk/py/test/rsession/reporter.py py/trunk/py/test/rsession/slave.py py/trunk/py/test/rsession/testing/test_executor.py py/trunk/py/test/rsession/testing/test_master.py py/trunk/py/test/rsession/testing/test_reporter.py py/trunk/py/test/rsession/testing/test_rsession.py py/trunk/py/test/rsession/testing/test_slave.py py/trunk/py/test/session.py py/trunk/py/test/terminal/terminal.py py/trunk/py/test/testing/test_collect.py py/trunk/py/test/testing/test_config.py py/trunk/py/test/testing/test_session.py Log: Intermediate checkin for some privatising of attributes Modified: py/trunk/py/test/collect.py ============================================================================== --- py/trunk/py/test/collect.py (original) +++ py/trunk/py/test/collect.py Sat Feb 3 19:29:24 2007 @@ -29,7 +29,7 @@ def configproperty(name): def fget(self): #print "retrieving %r property from %s" %(name, self.fspath) - return self.config.getvalue(name, self.fspath) + return self._config.getvalue(name, self.fspath) return property(fget) class Collector(object): @@ -43,8 +43,11 @@ """ def __init__(self, name, parent=None): self.name = name - self.parent = parent - self.config = getattr(parent, 'config', py.test.config) + self.parent = parent + self._config = getattr(parent, '_config', py.test.config) + if parent is not None: + if hasattr(parent, 'config'): + py.test.pdb() self.fspath = getattr(parent, 'fspath', None) Module = configproperty('Module') @@ -73,8 +76,8 @@ return not self == other def __cmp__(self, other): - s1 = self.getsortvalue() - s2 = other.getsortvalue() + s1 = self._getsortvalue() + s2 = other._getsortvalue() #print "cmp", s1, s2 return cmp(s1, s2) @@ -116,7 +119,7 @@ """ return a list of colitems for the given namelist. """ return [self.join(name) for name in namelist] - def getpathlineno(self): + def _getpathlineno(self): return self.fspath, py.std.sys.maxint def setup(self): @@ -139,7 +142,7 @@ def listnames(self): return [x.name for x in self.listchain()] - def getitembynames(self, namelist): + def _getitembynames(self, namelist): if isinstance(namelist, str): namelist = namelist.split("/") cur = self @@ -150,10 +153,10 @@ cur = next return cur - def haskeyword(self, keyword): + def _haskeyword(self, keyword): return keyword in self.name - def getmodpath(self): + def _getmodpath(self): """ return dotted module path (relative to the containing). """ inmodule = False newl = [] @@ -169,7 +172,7 @@ newl.append(x.name) return ".".join(newl) - def skipbykeyword(self, keyword): + def _skipbykeyword(self, keyword): """ raise Skipped() exception if the given keyword matches for this collector. """ @@ -184,8 +187,8 @@ py.test.skip("test not selected by keyword %r" %(keyword,)) def _matchonekeyword(self, key, chain): - for subitem in chain: - if subitem.haskeyword(key): + for subitem in chain: + if subitem._haskeyword(key): return True return False @@ -198,7 +201,7 @@ yieldtype = py.test.Item if isinstance(self, yieldtype): try: - self.skipbykeyword(keyword) + self._skipbykeyword(keyword) yield self except py.test.Item.Skipped: if reporterror is not None: @@ -220,21 +223,23 @@ excinfo = py.code.ExceptionInfo() reporterror((excinfo, self)) - def getsortvalue(self): + def _getsortvalue(self): return self.name captured_out = captured_err = None def startcapture(self): - return None # by default collectors don't capture output + return None # by default collectors don't capture output + def finishcapture(self): - return None # by default collectors don't capture output - def getouterr(self): + return None # by default collectors don't capture output + + def _getouterr(self): return self.captured_out, self.captured_err def _get_collector_trail(self): """ Shortcut """ - return self.config.get_collector_trail(self) + return self._config.get_collector_trail(self) class FSCollector(Collector): def __init__(self, fspath, parent=None): @@ -356,7 +361,7 @@ return res def startcapture(self): - if not self.config.option.nocapture: + if not self._config.option.nocapture: assert not hasattr(self, '_capture') self._capture = py.io.StdCaptureFD() @@ -418,7 +423,7 @@ teardown_class = getattr(teardown_class, 'im_func', teardown_class) teardown_class(self.obj) - def getsortvalue(self): + def _getsortvalue(self): # try to locate the class in the source - not nice, but probably # the most useful "solution" that we have try: @@ -432,7 +437,7 @@ pass # fall back... for x in self.tryiter((py.test.collect.Generator, py.test.Item)): - return x.getsortvalue() + return x._getsortvalue() class Instance(PyCollectorMixin, Collector): def _getobj(self): @@ -468,12 +473,12 @@ call, args = obj, () return call, args - def getpathlineno(self): + def _getpathlineno(self): code = py.code.Code(self.obj) return code.path, code.firstlineno - def getsortvalue(self): - return self.getpathlineno() + def _getsortvalue(self): + return self._getpathlineno() class DoctestFile(PyCollectorMixin, FSCollector): def run(self): Modified: py/trunk/py/test/config.py ============================================================================== --- py/trunk/py/test/config.py (original) +++ py/trunk/py/test/config.py Sat Feb 3 19:29:24 2007 @@ -75,14 +75,14 @@ assert path.check(), "%s: path does not exist" %(path,) col = self._getrootcollector(path) names = path.relto(col.fspath).split(path.sep) - return col.getitembynames(names) + return col._getitembynames(names) def _getrootcollector(self, path): pkgpath = path.pypkgpath() if pkgpath is None: pkgpath = path.check(file=1) and path.dirpath() or path col = self.conftest.rget("Directory", pkgpath)(pkgpath) - col.config = self + col._config = self return col def addoptions(self, groupname, *specs): Modified: py/trunk/py/test/item.py ============================================================================== --- py/trunk/py/test/item.py (original) +++ py/trunk/py/test/item.py Sat Feb 3 19:29:24 2007 @@ -32,7 +32,7 @@ class Item(py.test.collect.Collector): def startcapture(self): - if not self.config.option.nocapture: + if not self._config.option.nocapture: self._capture = py.io.StdCaptureFD() def finishcapture(self): @@ -57,13 +57,13 @@ def __repr__(self): return "<%s %r>" %(self.__class__.__name__, self.name) - def getpathlineno(self): + def _getpathlineno(self): code = py.code.Code(self.obj) return code.path, code.firstlineno - def getsortvalue(self): + def _getsortvalue(self): if self.sort_value is None: - return self.getpathlineno() + return self._getpathlineno() return self.sort_value def run(self): Modified: py/trunk/py/test/representation.py ============================================================================== --- py/trunk/py/test/representation.py (original) +++ py/trunk/py/test/representation.py Sat Feb 3 19:29:24 2007 @@ -43,13 +43,13 @@ """ This method represents py.test.Item info (path and module) """ root = item.fspath - modpath = item.getmodpath() + modpath = item._getmodpath() try: - fn, lineno = item.getpathlineno() + fn, lineno = item._getpathlineno() except TypeError: assert isinstance(item.parent, py.test.collect.Generator) # a generative test yielded a non-callable - fn, lineno = item.parent.getpathlineno() + fn, lineno = item.parent._getpathlineno() if root == fn: self.out.sep("_", "entrypoint: %s" %(modpath)) else: Modified: py/trunk/py/test/rsession/reporter.py ============================================================================== --- py/trunk/py/test/rsession/reporter.py (original) +++ py/trunk/py/test/rsession/reporter.py Sat Feb 3 19:29:24 2007 @@ -128,8 +128,6 @@ def repr_failure(self, item, outcome): excinfo = outcome.excinfo traceback = excinfo.traceback - #if item and not self.config.option.fulltrace: - # path, firstlineno = item.getpathlineno() if not traceback: self.out.line("empty traceback from item %r" % (item,)) return Modified: py/trunk/py/test/rsession/slave.py ============================================================================== --- py/trunk/py/test/rsession/slave.py (original) +++ py/trunk/py/test/rsession/slave.py Sat Feb 3 19:29:24 2007 @@ -52,11 +52,7 @@ self.pidinfo = pidinfo def execute(self, itemspec): - #item = self.rootcollector.getitembynames(itemspec) item = self.config._getcollector(itemspec) - #if isinstance(item, py.test.Function): - # ex = Executor(item.obj, setup=item.setup) - #else: ex = self.executor(item, config=self.config) if self.executor is AsyncExecutor: cont, pid = ex.execute() Modified: py/trunk/py/test/rsession/testing/test_executor.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_executor.py (original) +++ py/trunk/py/test/rsession/testing/test_executor.py Sat Feb 3 19:29:24 2007 @@ -94,7 +94,7 @@ def test_box_executor_stdout(): rootcol = py.test.collect.Directory(rootdir) - item = rootcol.getitembynames(funcprint_spec) + item = rootcol._getitembynames(funcprint_spec) ex = BoxExecutor(item, config=config) outcome_repr = ex.execute() outcome = ReprOutcome(outcome_repr) @@ -103,7 +103,7 @@ def test_box_executor_stdout_error(): rootcol = py.test.collect.Directory(rootdir) - item = rootcol.getitembynames(funcprintfail_spec) + item = rootcol._getitembynames(funcprintfail_spec) ex = BoxExecutor(item, config=config) outcome_repr = ex.execute() outcome = ReprOutcome(outcome_repr) @@ -112,7 +112,7 @@ def test_cont_executor(): rootcol = py.test.collect.Directory(rootdir) - item = rootcol.getitembynames(funcprintfail_spec) + item = rootcol._getitembynames(funcprintfail_spec) ex = AsyncExecutor(item, config=config) cont, pid = ex.execute() assert pid @@ -154,13 +154,13 @@ """)) rootcol = py.test.collect.Directory(tmpdir) tracer = Tracer() - item = rootcol.getitembynames("test_one.py/test_1") + item = rootcol._getitembynames("test_one.py/test_1") ex = ApigenExecutor(item, config=config) out1 = ex.execute(tracer) - item = rootcol.getitembynames("test_one.py/TestX/()/test_one") + item = rootcol._getitembynames("test_one.py/TestX/()/test_one") ex = ApigenExecutor(item, config=config) out2 = ex.execute(tracer) - item = rootcol.getitembynames("test_one.py/TestX/()/test_raise") + item = rootcol._getitembynames("test_one.py/TestX/()/test_raise") ex = ApigenExecutor(item, config=config) out3 = ex.execute(tracer) assert tracer.starts == 3 Modified: py/trunk/py/test/rsession/testing/test_master.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_master.py (original) +++ py/trunk/py/test/rsession/testing/test_master.py Sat Feb 3 19:29:24 2007 @@ -96,7 +96,7 @@ gw = py.execnet.PopenGateway() config = py.test.config._reparse([]) channel = setup_slave(gw, pkgdir, config) - spec = rootcol.getitembynames(funcpass_spec)._get_collector_trail() + spec = rootcol._getitembynames(funcpass_spec)._get_collector_trail() channel.send(spec) output = ReprOutcome(channel.receive()) assert output.passed @@ -124,8 +124,8 @@ return mn master_nodes = [open_gw(), open_gw(), open_gw()] - funcpass_item = rootcol.getitembynames(funcpass_spec) - funcfail_item = rootcol.getitembynames(funcfail_spec) + funcpass_item = rootcol._getitembynames(funcpass_spec) + funcfail_item = rootcol._getitembynames(funcfail_spec) itemgenerator = iter([funcfail_item] + [funcpass_item] * 5 + [funcfail_item] * 5) shouldstop = lambda : False @@ -153,7 +153,7 @@ mn, gw, channel = open_gw() rootcol = py.test.collect.Directory(pkgdir) - funchang_item = rootcol.getitembynames(funchang_spec) + funchang_item = rootcol._getitembynames(funchang_spec) mn.send(funchang_item) mn.send(StopIteration) # XXX: We have to wait here a bit to make sure that it really did happen Modified: py/trunk/py/test/rsession/testing/test_reporter.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_reporter.py (original) +++ py/trunk/py/test/rsession/testing/test_reporter.py Sat Feb 3 19:29:24 2007 @@ -64,7 +64,7 @@ config = py.test.config._reparse(["some_sub"]) # we just go... rootcol = py.test.collect.Directory(self.pkgdir.dirpath()) - item = rootcol.getitembynames(funcpass_spec) + item = rootcol._getitembynames(funcpass_spec) outcomes = self.prepare_outcomes() def boxfun(config, item, outcomes): @@ -84,8 +84,8 @@ config = py.test.config._reparse(["some_sub"]) # we just go... rootcol = py.test.collect.Directory(self.pkgdir.dirpath()) - funcitem = rootcol.getitembynames(funcpass_spec) - moditem = rootcol.getitembynames(mod_spec) + funcitem = rootcol._getitembynames(funcpass_spec) + moditem = rootcol._getitembynames(mod_spec) outcomes = self.prepare_outcomes() def boxfun(pkgdir, config, item, funcitem, outcomes): Modified: py/trunk/py/test/rsession/testing/test_rsession.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_rsession.py (original) +++ py/trunk/py/test/rsession/testing/test_rsession.py Sat Feb 3 19:29:24 2007 @@ -196,10 +196,10 @@ import ItemTestPassing, ItemTestFailing, ItemTestSkipping rootcol = py.test.collect.Directory(pkgdir.dirpath()) - itempass = rootcol.getitembynames(funcpass_spec) - itemfail = rootcol.getitembynames(funcfail_spec) - itemskip = rootcol.getitembynames(funcskip_spec) - itemprint = rootcol.getitembynames(funcprint_spec) + itempass = rootcol._getitembynames(funcpass_spec) + itemfail = rootcol._getitembynames(funcfail_spec) + itemskip = rootcol._getitembynames(funcskip_spec) + itemprint = rootcol._getitembynames(funcprint_spec) # actually run some tests for node in nodes: @@ -240,7 +240,7 @@ nodes = hm.init_hosts(allevents.append) rootcol = py.test.collect.Directory(pkgdir.dirpath()) - itempass = rootcol.getitembynames(funcoptioncustom_spec) + itempass = rootcol._getitembynames(funcoptioncustom_spec) for node in nodes: node.send(itempass) Modified: py/trunk/py/test/rsession/testing/test_slave.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_slave.py (original) +++ py/trunk/py/test/rsession/testing/test_slave.py Sat Feb 3 19:29:24 2007 @@ -61,7 +61,7 @@ def test_slave_run_passing(): node = gettestnode() - item = rootcol.getitembynames(funcpass_spec) + item = rootcol._getitembynames(funcpass_spec) outcome = node.execute(item._get_collector_trail()) assert outcome.passed assert not outcome.setupfailure @@ -73,7 +73,7 @@ def test_slave_run_failing(): node = gettestnode() - item = rootcol.getitembynames(funcfail_spec) + item = rootcol._getitembynames(funcfail_spec) outcome = node.execute(item._get_collector_trail()) assert not outcome.passed assert not outcome.setupfailure @@ -88,7 +88,7 @@ def test_slave_run_skipping(): node = gettestnode() - item = rootcol.getitembynames(funcskip_spec) + item = rootcol._getitembynames(funcskip_spec) outcome = node.execute(item._get_collector_trail()) assert not outcome.passed assert outcome.skipped @@ -100,7 +100,7 @@ def test_slave_run_failing_wrapped(): node = gettestnode() - item = rootcol.getitembynames(funcfail_spec) + item = rootcol._getitembynames(funcfail_spec) repr_outcome = node.run(item._get_collector_trail()) outcome = ReprOutcome(repr_outcome) assert not outcome.passed @@ -109,8 +109,8 @@ def test_slave_main_simple(): res = [] - failitem = rootcol.getitembynames(funcfail_spec) - passitem = rootcol.getitembynames(funcpass_spec) + failitem = rootcol._getitembynames(funcfail_spec) + passitem = rootcol._getitembynames(funcpass_spec) q = [None, passitem._get_collector_trail(), failitem._get_collector_trail() @@ -124,7 +124,7 @@ def test_slave_run_different_stuff(): node = gettestnode() - node.run(rootcol.getitembynames("py doc log.txt".split()). + node.run(rootcol._getitembynames("py doc log.txt".split()). _get_collector_trail()) def test_slave_setup_exit(): Modified: py/trunk/py/test/session.py ============================================================================== --- py/trunk/py/test/session.py (original) +++ py/trunk/py/test/session.py Sat Feb 3 19:29:24 2007 @@ -102,7 +102,7 @@ if self.config.option.collectonly and isinstance(colitem, py.test.Item): return if isinstance(colitem, py.test.Item): - colitem.skipbykeyword(self.config.option.keyword) + colitem._skipbykeyword(self.config.option.keyword) res = colitem.run() if res is None: return Passed() Modified: py/trunk/py/test/terminal/terminal.py ============================================================================== --- py/trunk/py/test/terminal/terminal.py (original) +++ py/trunk/py/test/terminal/terminal.py Sat Feb 3 19:29:24 2007 @@ -77,9 +77,9 @@ def start_Item(self, colitem): if self.config.option.verbose >= 1: if isinstance(colitem, py.test.Item): - realpath, lineno = colitem.getpathlineno() + realpath, lineno = colitem._getpathlineno() location = "%s:%d" % (realpath.basename, lineno+1) - self.out.write("%-20s %s " % (location, colitem.getmodpath())) + self.out.write("%-20s %s " % (location, colitem._getmodpath())) def finish(self, colitem, outcome): end = now() @@ -265,7 +265,7 @@ #print "repr_failures sees traceback" #py.std.pprint.pprint(traceback) if item and not self.config.option.fulltrace: - path, firstlineno = item.getpathlineno() + path, firstlineno = item._getpathlineno() ntraceback = traceback.cut(path=path, firstlineno=firstlineno) if ntraceback == traceback: ntraceback = ntraceback.cut(path=path) @@ -278,7 +278,7 @@ def repr_out_err(self, colitem): for parent in colitem.listchain(): - for name, obj in zip(['out', 'err'], parent.getouterr()): + for name, obj in zip(['out', 'err'], parent._getouterr()): if obj: self.out.sep("- ", "%s: recorded std%s" % (parent.name, name)) self.out.line(obj) Modified: py/trunk/py/test/testing/test_collect.py ============================================================================== --- py/trunk/py/test/testing/test_collect.py (original) +++ py/trunk/py/test/testing/test_collect.py Sat Feb 3 19:29:24 2007 @@ -19,9 +19,9 @@ col3 = col2.join('filetest.py') l = col3.listnames() assert len(l) == 3 - x = col1.getitembynames(l[1:]) + x = col1._getitembynames(l[1:]) assert x.name == "filetest.py" - x = col1.getitembynames("/".join(l[1:])) + x = col1._getitembynames("/".join(l[1:])) assert x.name == "filetest.py" l2 = x.listnames() assert len(l2) == 3 Modified: py/trunk/py/test/testing/test_config.py ============================================================================== --- py/trunk/py/test/testing/test_config.py (original) +++ py/trunk/py/test/testing/test_config.py Sat Feb 3 19:29:24 2007 @@ -272,7 +272,7 @@ col = colitems[0] assert isinstance(col, py.test.collect.Directory) for col in col.listchain(): - assert col.config is config + assert col._config is config def test_getcolitems_twodirs(self): config = py.test.config._reparse([self.tmpdir, self.tmpdir]) @@ -292,7 +292,7 @@ assert col2.name == 'a' for col in colitems: for subcol in col.listchain(): - assert col.config is config + assert col._config is config def test__getcol_global_file(self): x = self.tmpdir.ensure("x.py") @@ -303,7 +303,7 @@ assert col.parent.name == self.tmpdir.basename assert col.parent.parent is None for col in col.listchain(): - assert col.config is config + assert col._config is config def test__getcol_global_dir(self): x = self.tmpdir.ensure("a", dir=1) @@ -313,7 +313,7 @@ print col.listchain() assert col.name == 'a' assert col.parent is None - assert col.config is config + assert col._config is config def test__getcol_pkgfile(self): x = self.tmpdir.ensure("x.py") @@ -325,7 +325,7 @@ assert col.parent.name == x.dirpath().basename assert col.parent.parent is None for col in col.listchain(): - assert col.config is config + assert col._config is config def test_get_collector_trail_and_back(self): a = self.tmpdir.ensure("a", dir=1) Modified: py/trunk/py/test/testing/test_session.py ============================================================================== --- py/trunk/py/test/testing/test_session.py (original) +++ py/trunk/py/test/testing/test_session.py Sat Feb 3 19:29:24 2007 @@ -76,9 +76,9 @@ conftest = o.join('conftest.py').write(py.code.Source(""" import py class Class(py.test.collect.Class): - def haskeyword(self, keyword): + def _haskeyword(self, keyword): return keyword == 'xxx' or \ - super(Class, self).haskeyword(keyword) + super(Class, self)._haskeyword(keyword) """)) for keyword in ('xxx', 'xxx test_2', 'TestClass', 'xxx -test_1', 'TestClass test_2', 'xxx TestClass test_2',): @@ -206,7 +206,7 @@ assert int(err.strip()) == 23 assert isinstance(item.parent, py.test.collect.Module) - out, err = item.parent.getouterr() + out, err = item.parent._getouterr() assert out.find('module level output') != -1 allout = self.file.getvalue() print "allout:", allout From fijal at codespeak.net Sat Feb 3 19:44:38 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Sat, 3 Feb 2007 19:44:38 +0100 (CET) Subject: [py-svn] r37879 - in py/trunk/py/test: . rsession rsession/testing terminal testing Message-ID: <20070203184438.4A65A10070@code0.codespeak.net> Author: fijal Date: Sat Feb 3 19:44:34 2007 New Revision: 37879 Modified: py/trunk/py/test/collect.py py/trunk/py/test/item.py py/trunk/py/test/rsession/master.py py/trunk/py/test/rsession/reporter.py py/trunk/py/test/rsession/rest.py py/trunk/py/test/rsession/testing/test_reporter.py py/trunk/py/test/rsession/testing/test_rest.py py/trunk/py/test/rsession/testing/test_rsession.py py/trunk/py/test/rsession/web.py py/trunk/py/test/terminal/terminal.py py/trunk/py/test/testing/test_collect.py py/trunk/py/test/testing/test_doctest.py Log: s/tryiter/_tryiter/ Modified: py/trunk/py/test/collect.py ============================================================================== --- py/trunk/py/test/collect.py (original) +++ py/trunk/py/test/collect.py Sat Feb 3 19:44:34 2007 @@ -192,7 +192,7 @@ return True return False - def tryiter(self, yieldtype=None, reporterror=None, keyword=None): + def _tryiter(self, yieldtype=None, reporterror=None, keyword=None): """ yield stop item instances from flattening the collector. XXX deprecated: this way of iteration is not safe in all cases. @@ -213,7 +213,7 @@ if reporterror is not None: reporterror((None, self)) for x in self.run(): - for y in self.join(x).tryiter(yieldtype, + for y in self.join(x)._tryiter(yieldtype, reporterror, keyword): yield y except KeyboardInterrupt: @@ -226,7 +226,7 @@ def _getsortvalue(self): return self.name - captured_out = captured_err = None + _captured_out = _captured_err = None def startcapture(self): return None # by default collectors don't capture output @@ -234,7 +234,7 @@ return None # by default collectors don't capture output def _getouterr(self): - return self.captured_out, self.captured_err + return self._captured_out, self._captured_err def _get_collector_trail(self): """ Shortcut @@ -369,7 +369,7 @@ if hasattr(self, '_capture'): capture = self._capture del self._capture - self.captured_out, self.captured_err = capture.reset() + self._captured_out, self._captured_err = capture.reset() def __repr__(self): return "<%s %r>" % (self.__class__.__name__, self.name) @@ -436,7 +436,7 @@ except IOError: pass # fall back... - for x in self.tryiter((py.test.collect.Generator, py.test.Item)): + for x in self._tryiter((py.test.collect.Generator, py.test.Item)): return x._getsortvalue() class Instance(PyCollectorMixin, Collector): Modified: py/trunk/py/test/item.py ============================================================================== --- py/trunk/py/test/item.py (original) +++ py/trunk/py/test/item.py Sat Feb 3 19:44:34 2007 @@ -39,7 +39,7 @@ if hasattr(self, '_capture'): capture = self._capture del self._capture - self.captured_out, self.captured_err = capture.reset() + self._captured_out, self._captured_err = capture.reset() class Function(Item): Modified: py/trunk/py/test/rsession/master.py ============================================================================== --- py/trunk/py/test/rsession/master.py (original) +++ py/trunk/py/test/rsession/master.py Sat Feb 3 19:44:34 2007 @@ -36,7 +36,7 @@ def itemgen(colitems, reporter, keyword, reporterror): for x in colitems: - for y in x.tryiter(reporterror = lambda x: reporterror(reporter, x), keyword = keyword): + for y in x._tryiter(reporterror = lambda x: reporterror(reporter, x), keyword = keyword): yield y def randomgen(items, done_dict): Modified: py/trunk/py/test/rsession/reporter.py ============================================================================== --- py/trunk/py/test/rsession/reporter.py (original) +++ py/trunk/py/test/rsession/reporter.py Sat Feb 3 19:44:34 2007 @@ -288,7 +288,7 @@ # XXX This is a terrible hack, I don't like it # and will rewrite it at some point #self.count = 0 - lgt = len(list(item.tryiter())) + lgt = len(list(item._tryiter())) #self.lgt = lgt # print names relative to current workdir name = "/".join(item.listnames()) Modified: py/trunk/py/test/rsession/rest.py ============================================================================== --- py/trunk/py/test/rsession/rest.py (original) +++ py/trunk/py/test/rsession/rest.py Sat Feb 3 19:44:34 2007 @@ -66,7 +66,7 @@ def report_ItemStart(self, event): item = event.item if isinstance(item, py.test.collect.Module): - lgt = len(list(item.tryiter())) + lgt = len(list(item._tryiter())) lns = item.listnames()[1:] name = "/".join(lns) link = self.get_linkwriter().get_link(self.get_rootpath(item), Modified: py/trunk/py/test/rsession/testing/test_reporter.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_reporter.py (original) +++ py/trunk/py/test/rsession/testing/test_reporter.py Sat Feb 3 19:44:34 2007 @@ -123,7 +123,7 @@ rootcol = py.test.collect.Directory(tmpdir) hosts = [HostInfo('localhost')] r = self.reporter(config, hosts) - list(rootcol.tryiter(reporterror=lambda x : AbstractSession.reporterror(r.report, x))) + list(rootcol._tryiter(reporterror=lambda x : AbstractSession.reporterror(r.report, x))) cap = py.io.StdCaptureFD() boxfun() @@ -144,7 +144,7 @@ r = self.reporter(config, [host]) r.report(report.TestStarted([host])) r.report(report.RsyncFinished()) - list(rootcol.tryiter(reporterror=lambda x : AbstractSession.reporterror(r.report, x))) + list(rootcol._tryiter(reporterror=lambda x : AbstractSession.reporterror(r.report, x))) r.report(report.TestFinished()) cap = py.io.StdCaptureFD() Modified: py/trunk/py/test/rsession/testing/test_rest.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_rest.py (original) +++ py/trunk/py/test/rsession/testing/test_rest.py Sat Feb 3 19:44:34 2007 @@ -78,7 +78,7 @@ def __init__(self, parent): self.parent = parent self.fspath = py.path.local('.') - def tryiter(self): + def _tryiter(self): return ['test_foo', 'test_bar'] def listnames(self): return ['package', 'foo', 'bar.py'] Modified: py/trunk/py/test/rsession/testing/test_rsession.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_rsession.py (original) +++ py/trunk/py/test/rsession/testing/test_rsession.py Sat Feb 3 19:44:34 2007 @@ -68,7 +68,7 @@ pass """)) rootcol = py.test.collect.Directory(tmpdir) - data = list(rootcol.tryiter(reporterror=events.append)) + data = list(rootcol._tryiter(reporterror=events.append)) assert len(events) == 2 assert str(events[1][0].value) == "Reason" Modified: py/trunk/py/test/rsession/web.py ============================================================================== --- py/trunk/py/test/rsession/web.py (original) +++ py/trunk/py/test/rsession/web.py Sat Feb 3 19:44:34 2007 @@ -62,7 +62,7 @@ 'itemname': itemname} #if itemtype == 'Module': try: - d['length'] = str(len(list(event.item.tryiter()))) + d['length'] = str(len(list(event.item._tryiter()))) except: d['length'] = "?" return d Modified: py/trunk/py/test/terminal/terminal.py ============================================================================== --- py/trunk/py/test/terminal/terminal.py (original) +++ py/trunk/py/test/terminal/terminal.py Sat Feb 3 19:44:34 2007 @@ -64,8 +64,8 @@ and not self.config.option.collectonly): try: sum = 0 - for sub in subitems: - sum += len(list(colitem.join(sub).tryiter())) + for sub in subitems: + sum += len(list(colitem.join(sub)._tryiter())) except (SystemExit, KeyboardInterrupt): raise except: Modified: py/trunk/py/test/testing/test_collect.py ============================================================================== --- py/trunk/py/test/testing/test_collect.py (original) +++ py/trunk/py/test/testing/test_collect.py Sat Feb 3 19:44:34 2007 @@ -40,7 +40,7 @@ tmp.ensure('found_test.py') colitem = py.test.collect.Directory(tmp) - items = list(colitem.tryiter(py.test.collect.Module)) + items = list(colitem._tryiter(py.test.collect.Module)) assert len(items) == 2 items = [item.name for item in items] assert 'test_found.py' in items @@ -57,7 +57,7 @@ tmp.ensure('test_found.py') colitem = py.test.collect.Directory(tmp) - items = list(colitem.tryiter(py.test.collect.Module)) + items = list(colitem._tryiter(py.test.collect.Module)) assert len(items) == 2 for item in items: assert item.name == 'test_found.py' @@ -196,7 +196,7 @@ config = py.test.config._reparse([x]) #print "checking that %s returns custom items" % (x,) col = config._getcollector(x) - assert len(list(col.tryiter(py.test.Item))) == 2 + assert len(list(col._tryiter(py.test.Item))) == 2 #assert items[1].__class__.__name__ == 'MyFunction' # test that running a session works from the directories @@ -243,7 +243,7 @@ print "checking that %s returns custom items" % (x,) config = py.test.config._reparse([x]) col = config._getcollector(x) - assert len(list(col.tryiter(py.test.Item))) == 1 + assert len(list(col._tryiter(py.test.Item))) == 1 #assert items[1].__class__.__name__ == 'MyFunction' # test that running a session works from the directories @@ -319,13 +319,13 @@ try: conf.option.forcegen = 1 col = py.test.collect.Directory(rootdir) - x = list(col.tryiter(yieldtype=py.test.Function)) + x = list(col._tryiter(yieldtype=py.test.Function)) finally: conf.option.forcegen = old -def test_tryiter_ignores_skips(): - tmp = py.test.ensuretemp("tryiterskip") +def test__tryiter_ignores_skips(): + tmp = py.test.ensuretemp("_tryiterskip") tmp.ensure("subdir", "conftest.py").write(py.code.Source(""" import py class Directory(py.test.collect.Directory): @@ -334,7 +334,7 @@ """)) col = py.test.collect.Directory(tmp) try: - list(col.tryiter()) + list(col._tryiter()) except KeyboardInterrupt: raise except: @@ -342,14 +342,14 @@ py.test.fail("should not have raised: %s" %(exc,)) -def test_tryiter_ignores_failing_collectors(): - tmp = py.test.ensuretemp("tryiterfailing") +def test__tryiter_ignores_failing_collectors(): + tmp = py.test.ensuretemp("_tryiterfailing") tmp.ensure("subdir", "conftest.py").write(py.code.Source(""" bla bla bla """)) col = py.test.collect.Directory(tmp) try: - list(col.tryiter()) + list(col._tryiter()) except KeyboardInterrupt: raise except: @@ -357,7 +357,7 @@ py.test.fail("should not have raised: %s" %(exc,)) l = [] - list(col.tryiter(reporterror=l.append)) + list(col._tryiter(reporterror=l.append)) assert len(l) == 2 excinfo, item = l[-1] assert isinstance(excinfo, py.code.ExceptionInfo) @@ -368,7 +368,7 @@ raise KeyboardInterrupt() """)) col = py.test.collect.Directory(tmp) - py.test.raises(KeyboardInterrupt, list, col.tryiter()) + py.test.raises(KeyboardInterrupt, list, col._tryiter()) def test_check_random_inequality(): tmp = py.test.ensuretemp("ineq") @@ -376,7 +376,7 @@ pass """)) col = py.test.collect.Directory(tmp) - fn = col.tryiter().next() + fn = col._tryiter().next() assert fn != 3 assert fn != col assert fn != [1,2,3] @@ -399,7 +399,7 @@ tmp.ensure("__init__.py") col = py.test.collect.Module(tmp.join("test_one.py")) errors = [] - l = list(col.tryiter(reporterror=errors.append)) + l = list(col._tryiter(reporterror=errors.append)) assert len(errors) == 2 def test_check_collect_hashes(): @@ -420,7 +420,7 @@ """)) tmp.ensure("__init__.py") col = py.test.collect.Directory(tmp) - l = list(col.tryiter()) + l = list(col._tryiter()) assert len(l) == 4 for numi, i in enumerate(l): for numj, j in enumerate(l): Modified: py/trunk/py/test/testing/test_doctest.py ============================================================================== --- py/trunk/py/test/testing/test_doctest.py (original) +++ py/trunk/py/test/testing/test_doctest.py Sat Feb 3 19:44:34 2007 @@ -37,7 +37,7 @@ #print "checking that %s returns custom items" % (x,) config = py.test.config._reparse([x]) col = config._getcollector(x) - items = list(col.tryiter(py.test.Item)) + items = list(col._tryiter(py.test.Item)) assert len(items) == 1 assert isinstance(items[0], DoctestText) From hpk at codespeak.net Sat Feb 3 19:50:36 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 3 Feb 2007 19:50:36 +0100 (CET) Subject: [py-svn] r37880 - in py/trunk/py/test: . rsession Message-ID: <20070203185036.562FD10070@code0.codespeak.net> Author: hpk Date: Sat Feb 3 19:50:35 2007 New Revision: 37880 Modified: py/trunk/py/test/config.py py/trunk/py/test/rsession/hostmanage.py Log: removing public API from test.config (only one rather internal usage, anyway) Modified: py/trunk/py/test/config.py ============================================================================== --- py/trunk/py/test/config.py (original) +++ py/trunk/py/test/config.py Sat Feb 3 19:50:35 2007 @@ -118,14 +118,6 @@ except KeyError: return self.conftest.rget(name, path) - def getvalue_from_confpath(self, name, path): - """ same as previous, but returns only value from explicit - conftest path - """ - if isinstance(path, str): - path = py.path.local(path) - return self.conftest.rget_path(name, path) - def initsession(self): """ return an initialized session object. """ cls = self._getsessionclass() Modified: py/trunk/py/test/rsession/hostmanage.py ============================================================================== --- py/trunk/py/test/rsession/hostmanage.py (original) +++ py/trunk/py/test/rsession/hostmanage.py Sat Feb 3 19:50:35 2007 @@ -50,8 +50,8 @@ return False dir, base = os.path.split(path) try: - rsync_roots = self.config.getvalue_from_confpath("dist_rsyncroots", - dir) + name = "dist_rsyncroots" + rsync_roots = self.config.conftest.rget_path(name, dir) except AttributeError: rsync_roots = None if base == '.svn': From fijal at codespeak.net Sat Feb 3 19:52:23 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Sat, 3 Feb 2007 19:52:23 +0100 (CET) Subject: [py-svn] r37881 - in py/trunk/py: doc test test/terminal Message-ID: <20070203185223.A121410071@code0.codespeak.net> Author: fijal Date: Sat Feb 3 19:52:21 2007 New Revision: 37881 Modified: py/trunk/py/doc/TODO.txt py/trunk/py/test/collect.py py/trunk/py/test/item.py py/trunk/py/test/session.py py/trunk/py/test/terminal/terminal.py Log: Some other stuff goes private Modified: py/trunk/py/doc/TODO.txt ============================================================================== --- py/trunk/py/doc/TODO.txt (original) +++ py/trunk/py/doc/TODO.txt Sat Feb 3 19:52:21 2007 @@ -78,7 +78,7 @@ with help code from py.__.rest.directive.... make sure that the txt files in py/documentation/ use it -* private py.test not-meant-to-be-public API: +* (DONE) private py.test not-meant-to-be-public API: here is a rough list what we want public on collectors: py.test.collect.Collector. @@ -90,15 +90,17 @@ listnames() run() join() + multijoin() + name, parent, fspath * all collector class properties * and on py.test.Function|Item (which also has the collector interface): execute() - move all Outcome (Skipped/Passed/...) classes to + DONE move all Outcome (Skipped/Passed/...) classes to a global place (outcome.py?) - all other attributes of collectors shall become private + DONE all other attributes of collectors shall become private * after the above API cleanup there might be more :) Modified: py/trunk/py/test/collect.py ============================================================================== --- py/trunk/py/test/collect.py (original) +++ py/trunk/py/test/collect.py Sat Feb 3 19:52:21 2007 @@ -457,7 +457,7 @@ d = {} # slightly hackish to invoke setup-states on # collection ... - self.Function.state.prepare(self) + self.Function._state.prepare(self) for i, x in py.builtin.enumerate(self.obj()): call, args = self.getcallargs(x) if not callable(call): Modified: py/trunk/py/test/item.py ============================================================================== --- py/trunk/py/test/item.py (original) +++ py/trunk/py/test/item.py Sat Feb 3 19:52:21 2007 @@ -46,13 +46,13 @@ """ a Function Item is responsible for setting up and executing a Python callable test object. """ - state = SetupState() + _state = SetupState() def __init__(self, name, parent, args=(), obj=_dummy, sort_value = None): super(Function, self).__init__(name, parent) - self.args = args + self._args = args if obj is not _dummy: self._obj = obj - self.sort_value = sort_value + self._sort_value = sort_value def __repr__(self): return "<%s %r>" %(self.__class__.__name__, self.name) @@ -62,13 +62,13 @@ return code.path, code.firstlineno def _getsortvalue(self): - if self.sort_value is None: + if self._sort_value is None: return self._getpathlineno() - return self.sort_value + return self._sort_value def run(self): - self.state.prepare(self) - self.execute(self.obj, *self.args) + self._state.prepare(self) + self.execute(self.obj, *self._args) def execute(self, target, *args): """ default implementation for calling a test target is to Modified: py/trunk/py/test/session.py ============================================================================== --- py/trunk/py/test/session.py (original) +++ py/trunk/py/test/session.py Sat Feb 3 19:52:21 2007 @@ -20,7 +20,7 @@ def footer(self, colitems): """ teardown any resources after a test run. """ - py.test.Function.state.teardown_all() + py.test.Function._state.teardown_all() if not self.config.option.nomagic: py.magic.revoke(assertion=1) Modified: py/trunk/py/test/terminal/terminal.py ============================================================================== --- py/trunk/py/test/terminal/terminal.py (original) +++ py/trunk/py/test/terminal/terminal.py Sat Feb 3 19:52:21 2007 @@ -1,7 +1,6 @@ import py from time import time as now -Item = py.test.Item from py.__.test.terminal.out import getout from py.__.test.representation import Presenter from py.__.test.outcome import Skipped, Passed, Failed @@ -91,7 +90,7 @@ return colitem.elapsedtime = end - colitem.start if self.config.option.usepdb: - if isinstance(outcome, Item.Failed): + if isinstance(outcome, Failed): print "dispatching to ppdb", colitem self.repr_failure(colitem, outcome) import pdb From fijal at codespeak.net Sat Feb 3 21:16:05 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Sat, 3 Feb 2007 21:16:05 +0100 (CET) Subject: [py-svn] r37884 - in py/trunk/py/test: . rsession Message-ID: <20070203201605.E56C51006E@code0.codespeak.net> Author: fijal Date: Sat Feb 3 21:15:55 2007 New Revision: 37884 Modified: py/trunk/py/test/collect.py py/trunk/py/test/rsession/slave.py Log: Few missing Skipped Modified: py/trunk/py/test/collect.py ============================================================================== --- py/trunk/py/test/collect.py (original) +++ py/trunk/py/test/collect.py Sat Feb 3 21:15:55 2007 @@ -25,6 +25,7 @@ """ from __future__ import generators import py +from py.__.test.outcome import Skipped, Failed, Passed def configproperty(name): def fget(self): @@ -203,7 +204,7 @@ try: self._skipbykeyword(keyword) yield self - except py.test.Item.Skipped: + except Skipped: if reporterror is not None: excinfo = py.code.ExceptionInfo() reporterror((excinfo, self)) Modified: py/trunk/py/test/rsession/slave.py ============================================================================== --- py/trunk/py/test/rsession/slave.py (original) +++ py/trunk/py/test/rsession/slave.py Sat Feb 3 21:15:55 2007 @@ -5,6 +5,7 @@ import py from py.__.test.rsession.executor import RunExecutor, BoxExecutor, AsyncExecutor from py.__.test.rsession.outcome import Outcome +from py.__.test.outcome import Skipped import thread import os @@ -90,7 +91,7 @@ try: node = getnode(nextitem) res = node.run(nextitem) - except py.test.Item.Skipped, s: + except Skipped, s: send(Outcome(skipped=str(s)).make_repr()) except: excinfo = py.code.ExceptionInfo() From hpk at codespeak.net Sat Feb 3 21:33:14 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 3 Feb 2007 21:33:14 +0100 (CET) Subject: [py-svn] r37885 - py/trunk/py/test Message-ID: <20070203203314.6E3F61006E@code0.codespeak.net> Author: hpk Date: Sat Feb 3 21:33:12 2007 New Revision: 37885 Modified: py/trunk/py/test/collect.py py/trunk/py/test/item.py Log: adding docstrings, stripping non-used names from import Modified: py/trunk/py/test/collect.py ============================================================================== --- py/trunk/py/test/collect.py (original) +++ py/trunk/py/test/collect.py Sat Feb 3 21:33:12 2007 @@ -25,7 +25,7 @@ """ from __future__ import generators import py -from py.__.test.outcome import Skipped, Failed, Passed +from py.__.test.outcome import Skipped def configproperty(name): def fget(self): Modified: py/trunk/py/test/item.py ============================================================================== --- py/trunk/py/test/item.py (original) +++ py/trunk/py/test/item.py Sat Feb 3 21:33:12 2007 @@ -67,16 +67,16 @@ return self._sort_value def run(self): + """ setup and execute the underlying test function. """ self._state.prepare(self) self.execute(self.obj, *self._args) def execute(self, target, *args): - """ default implementation for calling a test target is to - simply call it. - """ + """ execute the given test function. """ target(*args) def setup(self): + """ perform setup for this test function. """ if getattr(self.obj, 'im_self', None): name = 'setup_method' else: @@ -87,6 +87,7 @@ return meth(self.obj) def teardown(self): + """ perform teardown for this test function. """ if getattr(self.obj, 'im_self', None): name = 'teardown_method' else: From hpk at codespeak.net Sat Feb 3 21:35:15 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 3 Feb 2007 21:35:15 +0100 (CET) Subject: [py-svn] r37886 - in py/trunk/py: doc test/rsession Message-ID: <20070203203515.90D7C10071@code0.codespeak.net> Author: hpk Date: Sat Feb 3 21:35:14 2007 New Revision: 37886 Modified: py/trunk/py/doc/test.txt py/trunk/py/test/rsession/hostmanage.py Log: hum, i thought i had refactored this option name already ... also the wrong name does not even cause a test failure. odd. Modified: py/trunk/py/doc/test.txt ============================================================================== --- py/trunk/py/doc/test.txt (original) +++ py/trunk/py/doc/test.txt Sat Feb 3 21:35:14 2007 @@ -702,7 +702,7 @@ Sample configuration:: dist_hosts = ['localhost', 'user at someserver:/tmp/somedir'] - dist_rsyncroots = ['../pypy', '../py'] + dist_rsync_roots = ['../pypy', '../py'] dist_remotepython = 'python2.4' dist_nicelevel = 10 dist_boxing = True Modified: py/trunk/py/test/rsession/hostmanage.py ============================================================================== --- py/trunk/py/test/rsession/hostmanage.py (original) +++ py/trunk/py/test/rsession/hostmanage.py Sat Feb 3 21:35:14 2007 @@ -50,7 +50,7 @@ return False dir, base = os.path.split(path) try: - name = "dist_rsyncroots" + name = "dist_rsync_roots" rsync_roots = self.config.conftest.rget_path(name, dir) except AttributeError: rsync_roots = None From hpk at codespeak.net Sun Feb 4 13:29:28 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 4 Feb 2007 13:29:28 +0100 (CET) Subject: [py-svn] r37898 - in py/trunk/py/test: . testing Message-ID: <20070204122928.ECCA510074@code0.codespeak.net> Author: hpk Date: Sun Feb 4 13:29:26 2007 New Revision: 37898 Modified: py/trunk/py/test/config.py py/trunk/py/test/conftesthandle.py py/trunk/py/test/testing/test_config.py py/trunk/py/test/testing/test_conftesthandle.py Log: provide a high-level helper for getting at a pathlist specified in a conftest (and the paths can be relative to the conftest.py file they are contained in) Modified: py/trunk/py/test/config.py ============================================================================== --- py/trunk/py/test/config.py (original) +++ py/trunk/py/test/config.py Sun Feb 4 13:29:26 2007 @@ -84,6 +84,19 @@ col = self.conftest.rget("Directory", pkgpath)(pkgpath) col._config = self return col + + def getvalue_pathlist(self, name, path=None): + """ return a matching value, which needs to be sequence + of filenames that will be returned as a list of Path + objects (they can be relative to the location + where they were found). + """ + try: + mod, relroots = self.conftest.rget_with_confmod(name, path) + except KeyError: + return None + modpath = py.path.local(mod.__file__).dirpath() + return [modpath.join(x, abs=True) for x in relroots] def addoptions(self, groupname, *specs): """ add a named group of options to the current testing session. @@ -163,6 +176,7 @@ def _reparse(self, args): """ this is used from tests that want to re-invoke parse(). """ + #assert args # XXX should not be empty global config_per_process oldconfig = py.test.config try: Modified: py/trunk/py/test/conftesthandle.py ============================================================================== --- py/trunk/py/test/conftesthandle.py (original) +++ py/trunk/py/test/conftesthandle.py Sun Feb 4 13:29:26 2007 @@ -41,63 +41,39 @@ except KeyError: dp = path.dirpath() if dp == path: - return [self.importconfig(defaultconftestpath)] + return [importconfig(defaultconftestpath)] clist = self.getconftestmodules(dp) conftestpath = path.join("conftest.py") if conftestpath.check(file=1): - clist.append(self.importconfig(conftestpath)) + clist.append(importconfig(conftestpath)) self._path2confmods[path] = clist # be defensive: avoid changes from caller side to # affect us by always returning a copy of the actual list return clist[:] - def getconftest(self, path): - """ Return a direct module of that path - """ - if isinstance(path, str): - path = py.path.local(path) - try: - conftestmod = self.getconftestmodules(path)[-1] - if py.path.local(conftestmod.__file__).dirpath() != path: - raise AttributeError - return conftestmod - except KeyError: - raise AttributeError - # we raise here AttributeError to unify error reporting in case - # of lack of variable in conftest or lack of file, but we do not want to - # hide ImportError - - # XXX no real use case, may probably go - #def lget(self, name, path=None): - # modules = self.getconftestmodules(path) - # return self._get(name, modules) - def rget(self, name, path=None): + mod, value = self.rget_with_confmod(name, path) + return value + + def rget_with_confmod(self, name, path=None): modules = self.getconftestmodules(path) modules.reverse() - return self._get(name, modules) - - def rget_path(self, name, path): - """ can raise AttributeError - """ - return getattr(self.getconftest(path), name) - - def _get(self, name, modules): for mod in modules: try: - return getattr(mod, name) + return mod, getattr(mod, name) except AttributeError: continue raise KeyError, name - def importconfig(self, configpath): - # We could have used caching here, but it's redundant since - # they're cached on path anyway, so we use it only when doing rget_path - if not configpath.dirpath('__init__.py').check(file=1): - # HACK: we don't want any "globally" imported conftest.py, - # prone to conflicts and subtle problems - modname = str(configpath).replace('.', configpath.sep) - mod = configpath.pyimport(modname=modname) - else: - mod = configpath.pyimport() - return mod +def importconfig(configpath): + # We could have used caching here, but it's redundant since + # they're cached on path anyway, so we use it only when doing rget_path + assert configpath.check(), configpath + if not configpath.dirpath('__init__.py').check(file=1): + # HACK: we don't want any "globally" imported conftest.py, + # prone to conflicts and subtle problems + modname = str(configpath).replace('.', configpath.sep) + mod = configpath.pyimport(modname=modname) + else: + mod = configpath.pyimport() + return mod Modified: py/trunk/py/test/testing/test_config.py ============================================================================== --- py/trunk/py/test/testing/test_config.py (original) +++ py/trunk/py/test/testing/test_config.py Sun Feb 4 13:29:26 2007 @@ -256,7 +256,19 @@ assert not config.is_boxed() config = py.test.config._reparse([tmpdir, '--box']) assert config.is_boxed() - + + def test_getvalue_pathlist(self): + tmpdir = self.tmpdir + somepath = tmpdir.join("x", "y", "z") + p = tmpdir.join("conftest.py") + p.write("pathlist = ['.', %r]" % str(somepath)) + config = py.test.config._reparse([p]) + assert config.getvalue_pathlist('notexist') is None + pl = config.getvalue_pathlist('pathlist') + print pl + assert len(pl) == 2 + assert pl[0] == tmpdir + assert pl[1] == somepath class TestConfigColitems: def setup_class(cls): Modified: py/trunk/py/test/testing/test_conftesthandle.py ============================================================================== --- py/trunk/py/test/testing/test_conftesthandle.py (original) +++ py/trunk/py/test/testing/test_conftesthandle.py Sun Feb 4 13:29:26 2007 @@ -65,14 +65,14 @@ #conftest.lget("a") == 1 #conftest.lget("b") == 1 - def test_value_access_path(self): + def test_value_access_with_confmod(self): topdir = self.basedir.join("adir", "b") topdir.ensure("xx", dir=True) conftest = Conftest(topdir) - assert conftest.rget_path("a", topdir) == 1.5 - assert conftest.rget_path("a", topdir.dirpath()) == 1 - py.test.raises(AttributeError, "conftest.rget_path('a', topdir.join('xx'))") - #assert py.path.local(mod.__file__).dirpath() == topdir + mod, value = conftest.rget_with_confmod("a", topdir) + assert value == 1.5 + path = py.path.local(mod.__file__) + assert path == self.basedir.join("adir", "b", "conftest.py") class TestConftestValueAccessInPackage(TestConftestValueAccessGlobal): def setup_class(cls): From arigo at codespeak.net Sun Feb 4 13:31:26 2007 From: arigo at codespeak.net (arigo at codespeak.net) Date: Sun, 4 Feb 2007 13:31:26 +0100 (CET) Subject: [py-svn] r37899 - py/trunk/py/c-extension/greenlet Message-ID: <20070204123126.1C86610077@code0.codespeak.net> Author: arigo Date: Sun Feb 4 13:31:24 2007 New Revision: 37899 Modified: py/trunk/py/c-extension/greenlet/greenlet.c Log: Try to print the exception before dying. Modified: py/trunk/py/c-extension/greenlet/greenlet.c ============================================================================== --- py/trunk/py/c-extension/greenlet/greenlet.c (original) +++ py/trunk/py/c-extension/greenlet/greenlet.c Sun Feb 4 13:31:24 2007 @@ -411,7 +411,8 @@ ts_self->stack_start = NULL; /* dead */ g_switch(ts_self->parent, result); /* must not return from here! */ - Py_FatalError("XXX memory exhausted at a very bad moment"); + PyErr_WriteUnraisable((PyObject*) ts_self); + Py_FatalError("greenlets cannot continue"); } /* back in the parent */ } From arigo at codespeak.net Sun Feb 4 14:01:23 2007 From: arigo at codespeak.net (arigo at codespeak.net) Date: Sun, 4 Feb 2007 14:01:23 +0100 (CET) Subject: [py-svn] r37901 - py/trunk/py/path/local Message-ID: <20070204130123.1E66610070@code0.codespeak.net> Author: arigo Date: Sun Feb 4 14:01:21 2007 New Revision: 37901 Modified: py/trunk/py/path/local/local.py Log: make_numbered_dir(): in a fork() situation, only the last process should remove the .lock, otherwise the other processes run the risk of seeing their temporary dir disappear. For now we remove the .lock in the parent only (i.e. we assume that the children finish before the parent). This is needed for long-running pypy translate.py processes using --fork-before. Modified: py/trunk/py/path/local/local.py ============================================================================== --- py/trunk/py/path/local/local.py (original) +++ py/trunk/py/path/local/local.py Sun Feb 4 14:01:21 2007 @@ -608,11 +608,19 @@ # put a .lock file in the new directory that will be removed at # process exit lockfile = udir.join('.lock') + mypid = os.getpid() if hasattr(lockfile, 'mksymlinkto'): - lockfile.mksymlinkto(str(os.getpid())) + lockfile.mksymlinkto(str(mypid)) else: - lockfile.write(str(os.getpid())) + lockfile.write(str(mypid)) def try_remove_lockfile(): + # in a fork() situation, only the last process should + # remove the .lock, otherwise the other processes run the + # risk of seeing their temporary dir disappear. For now + # we remove the .lock in the parent only (i.e. we assume + # that the children finish before the parent). + if os.getpid() != mypid: + return try: lockfile.remove() except py.error.Error: From hpk at codespeak.net Sun Feb 4 14:54:11 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 4 Feb 2007 14:54:11 +0100 (CET) Subject: [py-svn] r37908 - py/trunk/py/test/testing Message-ID: <20070204135411.F278710077@code0.codespeak.net> Author: hpk Date: Sun Feb 4 14:54:10 2007 New Revision: 37908 Modified: py/trunk/py/test/testing/test_conftesthandle.py Log: fixing test Modified: py/trunk/py/test/testing/test_conftesthandle.py ============================================================================== --- py/trunk/py/test/testing/test_conftesthandle.py (original) +++ py/trunk/py/test/testing/test_conftesthandle.py Sun Feb 4 14:54:10 2007 @@ -72,7 +72,8 @@ mod, value = conftest.rget_with_confmod("a", topdir) assert value == 1.5 path = py.path.local(mod.__file__) - assert path == self.basedir.join("adir", "b", "conftest.py") + assert path.dirpath() == self.basedir.join("adir", "b") + assert path.purebasename == "conftest" class TestConftestValueAccessInPackage(TestConftestValueAccessGlobal): def setup_class(cls): From hpk at codespeak.net Sun Feb 4 15:05:04 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 4 Feb 2007 15:05:04 +0100 (CET) Subject: [py-svn] r37909 - in py/trunk/py/test/rsession: . testing Message-ID: <20070204140504.6FA391006E@code0.codespeak.net> Author: hpk Date: Sun Feb 4 15:05:01 2007 New Revision: 37909 Added: py/trunk/py/test/rsession/testing/test_hostmanage.py - copied, changed from r37886, py/trunk/py/test/rsession/testing/test_rsync.py Removed: py/trunk/py/test/rsession/testing/test_rsync.py Modified: py/trunk/py/test/rsession/hostmanage.py py/trunk/py/test/rsession/master.py py/trunk/py/test/rsession/rsession.py py/trunk/py/test/rsession/slave.py py/trunk/py/test/rsession/testing/test_master.py py/trunk/py/test/rsession/testing/test_rest.py py/trunk/py/test/rsession/testing/test_rsession.py py/trunk/py/test/rsession/testing/test_slave.py Log: a much much much larger refactoring than i originally intended at this point: * HostManager and HostRSync are now acting more locally, also easier to test. * HostInfo deals with setting up gateways now * HostManager, HostRSync and HostInfo are all tested now in test_hostmanage.py (and do not involve a full startup of RSessions) * for rsyncing, the original directory structure (relative to config.topdir) is preserved on the other side, this makes "dist_rsync_roots" relatively clean now (but it doesn't pick up things on the fly, only initialises at the beginning) * added lots of tests * removed more occurences of pkgdir * streamlined and simplified some tests * removed lots of tests that do not appear to test specifically enough (and caused trouble for the refactoring) * removed lots of (but not all, i guess) test-specific functionality in hostmanage.py and a bit in rsession.py * removed HostOptions() in favour of rather directly accessing config values Modified: py/trunk/py/test/rsession/hostmanage.py ============================================================================== --- py/trunk/py/test/rsession/hostmanage.py (original) +++ py/trunk/py/test/rsession/hostmanage.py Sun Feb 4 15:05:01 2007 @@ -2,32 +2,59 @@ import py import time import thread, threading -from py.__.test.rsession.master import \ - setup_slave, MasterNode +from py.__.test.rsession.master import MasterNode +from py.__.test.rsession.slave import setup_slave + from py.__.test.rsession import report class HostInfo(object): """ Class trying to store all necessary attributes for host """ - host_ids = {} + _hostname2list = {} + localdest = None - def __init__(self, hostname, relpath=None): - self.hostid = self._getuniqueid(hostname) - self.hostname = hostname - self.relpath = relpath - - def _getuniqueid(cls, hostname): - if not hostname in cls.host_ids: - cls.host_ids[hostname] = 0 - return hostname - retval = hostname + '_' + str(cls.host_ids[hostname]) - cls.host_ids[hostname] += 1 - return retval - _getuniqueid = classmethod(_getuniqueid) + def __init__(self, spec): + parts = spec.split(':', 1) + self.hostname = parts.pop(0) + if parts: + self.relpath = parts[0] + else: + self.relpath = "pytestcache-" + self.hostname + self.hostid = self._getuniqueid(self.hostname) + + def _getuniqueid(self, hostname): + l = self._hostname2list.setdefault(hostname, []) + hostid = hostname + "_" * len(l) + l.append(hostid) + return hostid + + def initgateway(self, python="python"): + assert not hasattr(self, 'gw') + if self.hostname == "localhost": + gw = py.execnet.PopenGateway(python=python) + else: + gw = py.execnet.SshGateway(self.hostname, + remotepython=python) + self.gw = gw + channel = gw.remote_exec(""" + import os + targetdir = %r + if not os.path.isabs(targetdir): + homedir = os.environ['HOME'] + targetdir = os.path.join(homedir, targetdir) + if not os.path.exists(targetdir): + os.makedirs(targetdir) + channel.send(os.path.abspath(targetdir)) + """ % self.relpath) + self.gw_remotepath = channel.receive() + #print "initialized", gw, "with remotepath", self.gw_remotepath + if self.hostname == "localhost": + self.localdest = py.path.local(self.gw_remotepath) + assert self.localdest.check(dir=1) def __str__(self): - return "" % (self.hostname,) + return "" % (self.hostname, self.relpath) def __hash__(self): return hash(self.hostid) @@ -39,138 +66,77 @@ return not self == other class HostRSync(py.execnet.RSync): - """ An rsync wrapper which filters out *~, .svn/ and *.pyc + """ RSyncer that filters out common files """ - def __init__(self, config): - py.execnet.RSync.__init__(self, delete=True) - self.config = config + def __init__(self, *args, **kwargs): + self._synced = {} + super(HostRSync, self).__init__(*args, **kwargs) def filter(self, path): - if path.endswith('.pyc') or path.endswith('~'): - return False - dir, base = os.path.split(path) - try: - name = "dist_rsync_roots" - rsync_roots = self.config.conftest.rget_path(name, dir) - except AttributeError: - rsync_roots = None - if base == '.svn': - return False - if rsync_roots is None: - return True - return base in rsync_roots - -class DummyGateway(object): - pass - -class HostOptions(object): - """ Dummy container for host options, not to keep that - as different function parameters, mostly related to - tests only - """ - def __init__(self, remote_python="python", - optimise_localhost=True, do_sync=True, - create_gateways=True): - self.remote_python = remote_python - self.optimise_localhost = optimise_localhost - self.do_sync = do_sync - self.create_gateways = create_gateways + path = py.path.local(path) + if not path.ext in ('.pyc', '.pyo'): + if not path.basename.endswith('~'): + if path.check(dotfile=0): + return True + + def add_target_host(self, host, destrelpath=None, finishedcallback=None): + key = host.hostname, host.relpath + if key in self._synced: + if finishedcallback: + finishedcallback() + return False + self._synced[key] = True + # the follow attributes are set from host.initgateway() + gw = host.gw + remotepath = host.gw_remotepath + if destrelpath is not None: + remotepath = os.path.join(remotepath, destrelpath) + super(HostRSync, self).add_target(gw, + remotepath, + finishedcallback) + return True # added the target class HostManager(object): - def __init__(self, sshhosts, config, options=HostOptions()): + def __init__(self, sshhosts, config): self.sshhosts = sshhosts self.config = config - self.options = options - if not options.create_gateways: - self.prepare_gateways = self.prepare_dummy_gateways - #assert pkgdir.join("__init__.py").check(), ( - # "%s probably wrong" %(pkgdir,)) - - def prepare_dummy_gateways(self): - for host in self.sshhosts: - gw = DummyGateway() - host.gw = gw - gw.host = host - return self.sshhosts - - def prepare_ssh_gateway(self, host): - if self.options.remote_python is None: - gw = py.execnet.SshGateway(host.hostname) - else: - gw = py.execnet.SshGateway(host.hostname, - remotepython=self.options.remote_python) - return gw - - def prepare_popen_rsync_gateway(self, host): - """ Popen gateway, but with forced rsync - """ - from py.__.execnet.register import PopenCmdGateway - gw = PopenCmdGateway("cd ~; python -u -c 'exec input()'") - if not host.relpath.startswith("/"): - host.relpath = os.environ['HOME'] + '/' + host.relpath - return gw - - def prepare_popen_gateway(self, host): - if self.options.remote_python is None: - gw = py.execnet.PopenGateway() - else: - gw = py.execnet.PopenGateway(python=self.options.remote_python) - host.relpath = str(self.config.topdir) - return gw def prepare_gateways(self): + dist_remotepython = self.config.getvalue("dist_remotepython") for host in self.sshhosts: - if host.hostname == 'localhost': - if not self.options.optimise_localhost: - gw = self.prepare_popen_rsync_gateway(host) - else: - gw = self.prepare_popen_gateway(host) - else: - gw = self.prepare_ssh_gateway(host) - host.gw = gw - gw.host = host - return self.sshhosts - - def need_rsync(self, rsynced, hostname, remoterootpath): - if (hostname, remoterootpath) in rsynced: - return False - if hostname == 'localhost' and self.options.optimise_localhost: - return False - return True + host.initgateway(python=dist_remotepython) + host.gw.host = host # XXX would like to avoid it - def init_hosts(self, reporter, done_dict={}): + def init_rsync(self, reporter): + # send each rsync roots + roots = self.config.getvalue_pathlist("dist_rsync_roots") + if roots is None: + roots = [self.config.topdir] + self.prepare_gateways() + rsync = HostRSync() + for root in roots: + destrelpath = root.relto(self.config.topdir) + for host in self.sshhosts: + reporter(report.HostRSyncing(host)) + def donecallback(): + reporter(report.HostReady(host)) + rsync.add_target_host(host, destrelpath, + finishedcallback=donecallback) + rsync.send(root) + + def init_hosts(self, reporter, done_dict=None): if done_dict is None: done_dict = {} - - hosts = self.prepare_gateways() - - # rsyncing - rsynced = {} - - if self.options.do_sync: - rsync = HostRSync(self.config) - for host in hosts: - if not self.need_rsync(rsynced, host.hostname, host.relpath): - reporter(report.HostReady(host)) - continue - rsynced[(host.hostname, host.relpath)] = True - def done(host=host): - reporter(report.HostReady(host)) - reporter(report.HostRSyncing(host)) - if self.options.do_sync: - rsync.add_target(host.gw, host.relpath, done) - if not self.options.do_sync: - return # for testing only - rsync.send(self.config.topdir) # hosts ready + self.init_rsync(reporter) return self.setup_nodes(reporter, done_dict) def setup_nodes(self, reporter, done_dict): nodes = [] for host in self.sshhosts: - ch = setup_slave(host.gw, host.relpath, self.config) - nodes.append(MasterNode(ch, reporter, done_dict)) - + if hasattr(host.gw, 'remote_exec'): # otherwise dummy for tests :/ + ch = setup_slave(host, self.config) + nodes.append(MasterNode(ch, reporter, done_dict)) return nodes def teardown_hosts(self, reporter, channels, nodes, Modified: py/trunk/py/test/rsession/master.py ============================================================================== --- py/trunk/py/test/rsession/master.py (original) +++ py/trunk/py/test/rsession/master.py Sun Feb 4 15:05:01 2007 @@ -74,14 +74,3 @@ waiter() return all_tests -def setup_slave(gateway, pkgpath, config): - from py.__.test.rsession import slave - import os - ch = gateway.remote_exec(str(py.code.Source(slave.setup, "setup()"))) - #if hasattr(gateway, 'sshaddress'): - # assert not os.path.isabs(pkgpath) - ch.send(str(pkgpath)) - ch.send(config.make_repr(defaultconftestnames)) - return ch - -defaultconftestnames = ['dist_nicelevel'] Modified: py/trunk/py/test/rsession/rsession.py ============================================================================== --- py/trunk/py/test/rsession/rsession.py (original) +++ py/trunk/py/test/rsession/rsession.py Sun Feb 4 15:05:01 2007 @@ -10,9 +10,8 @@ from py.__.test.rsession import report from py.__.test.rsession.master import \ - setup_slave, MasterNode, dispatch_loop, itemgen, randomgen -from py.__.test.rsession.hostmanage import HostInfo, HostOptions, HostManager - + MasterNode, dispatch_loop, itemgen, randomgen +from py.__.test.rsession.hostmanage import HostInfo, HostManager from py.__.test.rsession.local import local_loop, plain_runner, apigen_runner,\ box_runner from py.__.test.rsession.reporter import LocalReporter, RemoteReporter @@ -24,24 +23,12 @@ An abstract session executes collectors/items through a runner. """ - def __init__(self, config, optimise_localhost=True): - super(AbstractSession, self).__init__(config=config) - self.optimise_localhost = optimise_localhost - def fixoptions(self): option = self.config.option if option.runbrowser and not option.startserver: #print "--runbrowser implies --startserver" option.startserver = True super(AbstractSession, self).fixoptions() - - def getpkgdir(path): - path = py.path.local(path) - pkgpath = path.pypkgpath() - if pkgpath is None: - pkgpath = path - return pkgpath - getpkgdir = staticmethod(getpkgdir) def init_reporter(self, reporter, sshhosts, reporter_class, arg=""): """ This initialises so called `reporter` class, which will @@ -115,18 +102,6 @@ self.checkfun = checkfun return new_reporter, checkfun -def parse_directories(sshhosts): - """ Parse sshadresses of hosts to have distinct hostname/hostdir - """ - directories = {} - for host in sshhosts: - m = re.match("^(.*?):(.*)$", host.hostname) - if m: - host.hostname = m.group(1) - host.relpath = m.group(2) + "-" + host.hostname - else: - host.relpath = "pytestcache-%s" % host.hostname - class RSession(AbstractSession): """ Remote version of session """ @@ -151,7 +126,7 @@ """ main loop for running tests. """ args = self.config.args - sshhosts, remotepython = self.read_distributed_config() + sshhosts = self._getconfighosts() reporter, startserverflag = self.init_reporter(reporter, sshhosts, RemoteReporter) reporter, checkfun = self.wrap_reporter(reporter) @@ -159,9 +134,7 @@ reporter(report.TestStarted(sshhosts)) done_dict = {} - hostopts = HostOptions(remote_python=remotepython, - optimise_localhost=self.optimise_localhost) - hostmanager = HostManager(sshhosts, self.config, hostopts) + hostmanager = HostManager(sshhosts, self.config) try: nodes = hostmanager.init_hosts(reporter, done_dict) reporter(report.RsyncFinished()) @@ -191,14 +164,9 @@ self.kill_server(startserverflag) raise - def read_distributed_config(self): - """ Read from conftest file the configuration of distributed testing - """ - sshhosts = [HostInfo(i) for i in - self.config.getvalue("dist_hosts")] - parse_directories(sshhosts) - remotepython = self.config.getvalue("dist_remotepython") - return sshhosts, remotepython + def _getconfighosts(self): + return [HostInfo(spec) for spec in + self.config.getvalue("dist_hosts")] def dispatch_tests(self, nodes, reporter, checkfun, done_dict): colitems = self.config.getcolitems() @@ -262,7 +230,7 @@ print >>sys.stderr, 'building documentation' capture = py.io.StdCaptureFD() try: - pkgdir = self.getpkgdir(self.config.args[0]) + pkgdir = py.path.local(self.config.args[0]).pypkgpath() apigen.build(pkgdir, DocStorageAccessor(self.docstorage), capture) @@ -272,7 +240,7 @@ def init_runner(self): if self.config.option.apigen: from py.__.apigen.tracer.tracer import Tracer, DocStorage - pkgdir = self.getpkgdir(self.config.args[0]) + pkgdir = py.path.local(self.config.args[0]).pypkgpath() apigen = py.path.local(self.config.option.apigen).pyimport() if not hasattr(apigen, 'get_documentable_items'): raise NotImplementedError("Provided script does not seem " @@ -288,4 +256,3 @@ return box_runner return plain_runner - Modified: py/trunk/py/test/rsession/slave.py ============================================================================== --- py/trunk/py/test/rsession/slave.py (original) +++ py/trunk/py/test/rsession/slave.py Sun Feb 4 15:05:01 2007 @@ -106,6 +106,15 @@ while nextitem is not None: nextitem = receive() +defaultconftestnames = ['dist_nicelevel'] +def setup_slave(host, config): + channel = host.gw.remote_exec(str(py.code.Source(setup, "setup()"))) + configrepr = config.make_repr(defaultconftestnames) + #print "sending configrepr", configrepr + channel.send(host.gw_remotepath) + channel.send(configrepr) + return channel + def setup(): def callback_gen(channel, queue, info): def callback(item): @@ -116,19 +125,16 @@ sys.exit(0) queue.put(item) return callback - + # our current dir is the topdir import os, sys - basedir = channel.receive() # path is ready + basedir = channel.receive() config_repr = channel.receive() # setup defaults... sys.path.insert(0, basedir) import py config = py.test.config - if config._initialized: - config = config._reparse([basedir]) - config.merge_repr(config_repr) - else: - config.initdirect(basedir, config_repr) + assert not config._initialized + config.initdirect(basedir, config_repr) if not config.option.nomagic: py.magic.invoke(assertion=1) from py.__.test.rsession.slave import slave_main, PidInfo Copied: py/trunk/py/test/rsession/testing/test_hostmanage.py (from r37886, py/trunk/py/test/rsession/testing/test_rsync.py) ============================================================================== --- py/trunk/py/test/rsession/testing/test_rsync.py (original) +++ py/trunk/py/test/rsession/testing/test_hostmanage.py Sun Feb 4 15:05:01 2007 @@ -3,28 +3,135 @@ """ import py -from py.__.test.rsession.hostmanage import HostRSync +from py.__.test.rsession.hostmanage import HostRSync +from py.__.test.rsession.hostmanage import HostInfo, HostManager -def test_rsync(): - tmpdir = py.test.ensuretemp("rsync_rsession") - tmpdir.ensure("a", dir=True) - tmpdir.ensure("b", dir=True) - tmpdir.ensure("conftest.py").write(py.code.Source(""" - dist_rsyncroots = ['a'] - """)) - tmpdir.join("a").ensure("x") - adir = tmpdir.join("a").ensure("xy", dir=True) - adir.ensure("conftest.py").write(py.code.Source(""" - dist_rsyncroots = ['b', 'conftest.py'] - """)) - adir.ensure("a", dir=True) - adir.ensure("b", dir=True) - config = py.test.config._reparse([str(tmpdir)]) - h = HostRSync(config) - h.sourcedir = config.topdir - assert h.filter(str(tmpdir.join("a"))) - assert not h.filter(str(tmpdir.join("b"))) - assert h.filter(str(tmpdir.join("a").join("x"))) - assert h.filter(str(adir.join("conftest.py"))) - assert not h.filter(str(adir.join("a"))) - assert h.filter(str(adir.join("b"))) +class DirSetup: + def setup_method(self, method): + name = "%s.%s" %(self.__class__.__name__, method.func_name) + self.tmpdir = py.test.ensuretemp(name) + self.source = self.tmpdir.ensure("source", dir=1) + self.dest = self.tmpdir.join("dest") + +class TestHostInfo: + def test_defaultpath(self): + x = HostInfo("localhost") + assert x.hostname == "localhost" + assert x.relpath == "pytestcache-" + x.hostname + + def test_path(self): + x = HostInfo("localhost:/tmp") + assert x.relpath == "/tmp" + assert x.hostname == "localhost" + + def test_hostid(self): + x = HostInfo("localhost") + y = HostInfo("localhost") + assert x.hostid != y.hostid + x = HostInfo("localhost:/tmp") + y = HostInfo("localhost") + assert x.hostid != y.hostid + + def test_non_existing_hosts(self): + host = HostInfo("alskdjalsdkjasldkajlsd") + py.test.raises((py.process.cmdexec.Error, IOError, EOFError), + host.initgateway) + + def test_initgateway_localhost_relpath(self): + name = "pytestcache-localhost" + x = HostInfo("localhost:%s" % name) + x.initgateway() + assert x.gw + try: + homedir = py.path.local(py.std.os.environ['HOME']) + expected = homedir.join(name) + assert x.gw_remotepath == str(expected) + assert x.localdest == expected + finally: + x.gw.exit() + + + def test_initgateway_ssh_and_remotepath(self): + option = py.test.config.option + if option.sshtarget is None: + py.test.skip("no known ssh target, use -S to set one") + x = HostInfo("%s" % (option.sshtarget, )) + x.initgateway() + assert x.gw + assert x.gw_remotepath.endswith(x.relpath) + channel = x.gw.remote_exec(""" + import os + homedir = os.environ['HOME'] + relpath = channel.receive() + path = os.path.join(homedir, relpath) + channel.send(path) + """) + channel.send(x.relpath) + res = channel.receive() + assert res == x.gw_remotepath + assert x.localdest is None + +class TestSyncing(DirSetup): + def test_hrsync_filter(self): + self.source.ensure("dir", "file.txt") + self.source.ensure(".svn", "entries") + self.source.ensure(".somedotfile", "moreentries") + self.source.ensure("somedir", "editfile~") + syncer = HostRSync() + l = list(self.source.visit(rec=syncer.filter, + fil=syncer.filter)) + assert len(l) == 3 + basenames = [x.basename for x in l] + assert 'dir' in basenames + assert 'file.txt' in basenames + assert 'somedir' in basenames + + def test_hrsync_one_host(self): + h1 = HostInfo("localhost:%s" % self.dest) + finished = [] + rsync = HostRSync() + h1.initgateway() + rsync.add_target_host(h1) + self.source.join("hello.py").write("world") + rsync.send(self.source) + assert self.dest.join("hello.py").check() + + def test_hrsync_same_host_twice(self): + h1 = HostInfo("localhost:%s" % self.dest) + h2 = HostInfo("localhost:%s" % self.dest) + finished = [] + rsync = HostRSync() + l = [] + h1.initgateway() + res1 = rsync.add_target_host(h1) + assert res1 + res2 = rsync.add_target_host(h2) + assert not res2 + +class TestHostManager(DirSetup): + def test_hostmanager_init_rsync_topdir(self): + dir2 = self.source.ensure("dir1", "dir2", dir=1) + dir2.ensure("hello") + config = py.test.config._reparse([self.source]) + hm = HostManager([HostInfo("localhost:" + str(self.dest))], config) + events = [] + hm.init_rsync(reporter=events.append) + assert self.dest.join("dir1").check() + assert self.dest.join("dir1", "dir2").check() + assert self.dest.join("dir1", "dir2", 'hello').check() + + def test_hostmanager_init_rsync_rsync_roots(self): + dir2 = self.source.ensure("dir1", "dir2", dir=1) + dir2.ensure("hello") + self.source.ensure("bogusdir", "file") + self.source.join("conftest.py").write(py.code.Source(""" + dist_rsync_roots = ['dir1/dir2'] + """)) + config = py.test.config._reparse([self.source]) + hm = HostManager([HostInfo("localhost:" + str(self.dest))], config) + events = [] + hm.init_rsync(reporter=events.append) + assert self.dest.join("dir1").check() + assert self.dest.join("dir1", "dir2").check() + assert self.dest.join("dir1", "dir2", 'hello').check() + assert not self.dest.join("bogus").check() Modified: py/trunk/py/test/rsession/testing/test_master.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_master.py (original) +++ py/trunk/py/test/rsession/testing/test_master.py Sun Feb 4 15:05:01 2007 @@ -9,18 +9,19 @@ if sys.platform == 'win32': py.test.skip("rsession is unsupported on Windows.") -from py.__.test.rsession.master import dispatch_loop, setup_slave, MasterNode, randomgen +from py.__.test.rsession.master import dispatch_loop, MasterNode, randomgen +from py.__.test.rsession.slave import setup_slave from py.__.test.rsession.outcome import ReprOutcome, Outcome -from py.__.test.rsession.testing.test_slave import funcpass_spec, funcfail_spec, funchang_spec from py.__.test.rsession import report from py.__.test.rsession.hostmanage import HostInfo def setup_module(mod): # bind an empty config - config = py.test.config._reparse([]) + mod.tmpdir = tmpdir = py.test.ensuretemp(mod.__name__) + # to avoid rsyncing + config = py.test.config._reparse([tmpdir]) config._overwrite('dist_taskspernode', 10) - mod.pkgdir = py.path.local(py.__file__).dirpath().dirpath() - mod.rootcol = py.test.collect.Directory(mod.pkgdir) + mod.rootcol = config._getcollector(tmpdir) class DummyGateway(object): def __init__(self): @@ -92,46 +93,69 @@ node.pending.pop() dispatch_loop(masternodes, itemgenerator, shouldstop, waiter=waiter) -def test_slave_setup(): - gw = py.execnet.PopenGateway() - config = py.test.config._reparse([]) - channel = setup_slave(gw, pkgdir, config) - spec = rootcol._getitembynames(funcpass_spec)._get_collector_trail() - channel.send(spec) - output = ReprOutcome(channel.receive()) - assert output.passed - channel.send(42) - channel.waitclose(10) - gw.exit() - -def test_slave_running(): - def simple_report(event): - if not isinstance(event, report.ReceivedItemOutcome): - return - item = event.item - if item.code.name == 'funcpass': - assert event.outcome.passed - else: - assert not event.outcome.passed - - def open_gw(): - gw = py.execnet.PopenGateway() - gw.host = HostInfo("localhost") - gw.host.gw = gw - config = py.test.config._reparse([]) - channel = setup_slave(gw, pkgdir, config) - mn = MasterNode(channel, simple_report, {}) - return mn - - master_nodes = [open_gw(), open_gw(), open_gw()] - funcpass_item = rootcol._getitembynames(funcpass_spec) - funcfail_item = rootcol._getitembynames(funcfail_spec) - itemgenerator = iter([funcfail_item] + - [funcpass_item] * 5 + [funcfail_item] * 5) - shouldstop = lambda : False - dispatch_loop(master_nodes, itemgenerator, shouldstop) +class TestSlave: + def setup_class(cls): + cls.tmpdir = tmpdir = py.test.ensuretemp(cls.__name__) + pkgpath = tmpdir.join("pkg") + pkgpath.ensure("__init__.py") + pkgpath.join("test_something.py").write(py.code.Source(""" + def funcpass(): + pass + + def funcfail(): + raise AssertionError("hello world") + """)) + cls.config = py.test.config._reparse([tmpdir]) + assert cls.config.topdir == tmpdir + cls.rootcol = cls.config._getcollector(tmpdir) + + def _gettrail(self, *names): + item = self.rootcol._getitembynames(names) + return self.config.get_collector_trail(item) + + def test_slave_setup(self): + host = HostInfo("localhost:%s" %(self.tmpdir,)) + host.initgateway() + channel = setup_slave(host, self.config) + spec = self._gettrail("pkg", "test_something.py", "funcpass") + print "sending", spec + channel.send(spec) + output = ReprOutcome(channel.receive()) + assert output.passed + channel.send(42) + channel.waitclose(10) + host.gw.exit() + + def test_slave_running(self): + py.test.skip("XXX test broken, needs refactoring") + def simple_report(event): + if not isinstance(event, report.ReceivedItemOutcome): + return + item = event.item + if item.code.name == 'funcpass': + assert event.outcome.passed + else: + assert not event.outcome.passed + + def open_gw(): + gw = py.execnet.PopenGateway() + gw.host = HostInfo("localhost") + gw.host.gw = gw + config = py.test.config._reparse([tmpdir]) + channel = setup_slave(gw.host, config) + mn = MasterNode(channel, simple_report, {}) + return mn + + master_nodes = [open_gw(), open_gw(), open_gw()] + funcpass_item = rootcol._getitembynames(funcpass_spec) + funcfail_item = rootcol._getitembynames(funcfail_spec) + itemgenerator = iter([funcfail_item] + + [funcpass_item] * 5 + [funcfail_item] * 5) + shouldstop = lambda : False + dispatch_loop(master_nodes, itemgenerator, shouldstop) def test_slave_running_interrupted(): + py.test.skip("XXX test broken, needs refactoring") #def simple_report(event): # if not isinstance(event, report.ReceivedItemOutcome): # return @@ -146,7 +170,7 @@ gw = py.execnet.PopenGateway() gw.host = HostInfo("localhost") gw.host.gw = gw - config = py.test.config._reparse([]) + config = py.test.config._reparse([tmpdir]) channel = setup_slave(gw, pkgdir, config) mn = MasterNode(channel, reports.append, {}) return mn, gw, channel Modified: py/trunk/py/test/rsession/testing/test_rest.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_rest.py (original) +++ py/trunk/py/test/rsession/testing/test_rest.py Sun Feb 4 15:05:01 2007 @@ -52,7 +52,7 @@ 'localhost\n\n') def test_report_HostRSyncing(self): - event = report.HostRSyncing(HostInfo('localhost', '/foo/bar')) + event = report.HostRSyncing(HostInfo('localhost:/foo/bar')) reporter.report(event) assert stdout.getvalue() == ('::\n\n localhost: RSYNC ==> ' '/foo/bar\n\n') Modified: py/trunk/py/test/rsession/testing/test_rsession.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_rsession.py (original) +++ py/trunk/py/test/rsession/testing/test_rsession.py Sun Feb 4 15:05:01 2007 @@ -4,38 +4,16 @@ import py from py.__.test.rsession import report -from py.__.test.rsession.rsession import RSession, parse_directories,\ - parse_directories -from py.__.test.rsession.hostmanage import HostOptions, HostManager,\ - HostInfo +from py.__.test.rsession.rsession import RSession +from py.__.test.rsession.hostmanage import HostManager, HostInfo from py.__.test.rsession.testing.test_slave import funcfail_spec,\ funcpass_spec, funcskip_spec, funcprint_spec, funcprintfail_spec, \ funcoptioncustom_spec def setup_module(mod): mod.pkgdir = py.path.local(py.__file__).dirpath() + mod.tmpdir = py.test.ensuretemp(mod.__name__) -def test_setup_non_existing_hosts(): - setup_events = [] - hosts = [HostInfo("alskdjalsdkjasldkajlsd")] - config = py.test.config._reparse([]) - hm = HostManager(hosts, config) - cmd = "hm.init_hosts(setup_events.append)" - py.test.raises((py.process.cmdexec.Error, IOError, EOFError), cmd) - #assert setup_events - -def test_getpkdir(): - one = pkgdir.join("initpkg.py") - two = pkgdir.join("path", "__init__.py") - p1 = RSession.getpkgdir(one) - p2 = RSession.getpkgdir(two) - assert p1 == p2 - assert p1 == pkgdir - -def test_getpkdir_no_inits(): - tmp = py.test.ensuretemp("getpkdir1") - fn = tmp.ensure("hello.py") - assert RSession.getpkgdir(fn) == fn #def test_make_colitems(): # one = pkgdir.join("initpkg.py") @@ -74,10 +52,11 @@ class TestRSessionRemote: def test_example_distribution_minus_x(self): + destdir = py.test.ensuretemp("example_dist_dest_x") tmpdir = py.test.ensuretemp("example_distribution_minus_x") tmpdir.ensure("sub", "conftest.py").write(py.code.Source(""" - dist_hosts = [%r] - """ % ('localhost',))) + dist_hosts = ['localhost:%s'] + """ % destdir)) tmpdir.ensure("sub", "__init__.py") tmpdir.ensure("sub", "test_one.py").write(py.code.Source(""" def test_1(): @@ -92,8 +71,7 @@ def test_4(someargs): pass """)) - args = [str(tmpdir.join("sub")), "-x"] - config = py.test.config._reparse(args) + config = py.test.config._reparse([tmpdir.join("sub"), '-x']) rsession = RSession(config) allevents = [] rsession.main(reporter=allevents.append) @@ -102,13 +80,14 @@ assert len(testevents) == 3 assert rsession.checkfun() - def test_example_distribution(self): + def test_distribution_rsync_roots_example(self): + destdir = py.test.ensuretemp("example_dist_destdir") subdir = "sub_example_dist" tmpdir = py.test.ensuretemp("example_distribution") tmpdir.ensure(subdir, "conftest.py").write(py.code.Source(""" - dist_hosts = [%r] + dist_hosts = ["localhost:%s"] dist_rsync_roots = ["%s", "../py"] - """ % ('localhost', tmpdir.join(subdir), ))) + """ % (destdir, tmpdir.join(subdir), ))) tmpdir.ensure(subdir, "__init__.py") tmpdir.ensure(subdir, "test_one.py").write(py.code.Source(""" def test_1(): @@ -124,16 +103,18 @@ def test_6(): import py assert py.__file__ != '%s' - """ % (str(tmpdir.join(subdir)), py.__file__))) - tmpdir.join("py").mksymlinkto(py.path.local(py.__file__).dirpath()) - args = [str(tmpdir.join(subdir))] - config = py.test.config._reparse(args) - rsession = RSession(config, optimise_localhost=False) + """ % (tmpdir.join(subdir), py.__file__))) + destdir.join("py").mksymlinkto(py.path.local(py.__file__).dirpath()) + config = py.test.config._reparse([tmpdir.join(subdir)]) + assert config.topdir == tmpdir + assert not tmpdir.join("__init__.py").check() + rsession = RSession(config) allevents = [] rsession.main(reporter=allevents.append) testevents = [x for x in allevents if isinstance(x, report.ReceivedItemOutcome)] assert len(testevents) + print testevents passevents = [i for i in testevents if i.outcome.passed] failevents = [i for i in testevents if i.outcome.excinfo] skippedevents = [i for i in testevents if i.outcome.skipped] @@ -156,13 +137,11 @@ def test_setup_teardown_ssh(self): hosts = [HostInfo('localhost')] - parse_directories(hosts) setup_events = [] teardown_events = [] - config = py.test.config._reparse([]) - opts = HostOptions(optimise_localhost=False) - hm = HostManager(hosts, config, opts) + config = py.test.config._reparse([tmpdir]) + hm = HostManager(hosts, config) nodes = hm.init_hosts(setup_events.append) hm.teardown_hosts(teardown_events.append, [node.channel for node in nodes], nodes) @@ -184,12 +163,10 @@ def test_setup_teardown_run_ssh(self): hosts = [HostInfo('localhost')] - parse_directories(hosts) allevents = [] config = py.test.config._reparse([]) - opts = HostOptions(optimise_localhost=False) - hm = HostManager(hosts, config, opts) + hm = HostManager(hosts, config) nodes = hm.init_hosts(allevents.append) from py.__.test.rsession.testing.test_executor \ @@ -223,47 +200,11 @@ passed_stdout = [i for i in passed if i.outcome.stdout.find('samfing') != -1] assert len(passed_stdout) == len(nodes), passed - def test_config_pass(self): - """ Tests options object passing master -> server - """ - allevents = [] - hosts = [HostInfo('localhost')] - parse_directories(hosts) - config = py.test.config._reparse([]) - config._overwrite('custom', 'custom') - # we need to overwrite default list to serialize - from py.__.test.rsession.master import defaultconftestnames - defaultconftestnames.append("custom") - try: - opts = HostOptions(optimise_localhost=False) - hm = HostManager(hosts, config, opts) - nodes = hm.init_hosts(allevents.append) - - rootcol = py.test.collect.Directory(pkgdir.dirpath()) - itempass = rootcol._getitembynames(funcoptioncustom_spec) - - for node in nodes: - node.send(itempass) - - hm.teardown_hosts(allevents.append, [node.channel for node in nodes], nodes) - events = [i for i in allevents - if isinstance(i, report.ReceivedItemOutcome)] - passed = [i for i in events - if i.outcome.passed] - skipped = [i for i in events - if i.outcome.skipped] - assert len(passed) == 1 * len(nodes) - assert len(skipped) == 0 - assert len(events) == len(passed) - finally: - defaultconftestnames.remove("custom") - def test_nice_level(self): """ Tests if nice level behaviour is ok """ allevents = [] hosts = [HostInfo('localhost')] - parse_directories(hosts) tmpdir = py.test.ensuretemp("nice") tmpdir.ensure("__init__.py") tmpdir.ensure("conftest.py").write(py.code.Source(""" @@ -283,45 +224,6 @@ if isinstance(x, report.ReceivedItemOutcome)] passevents = [x for x in testevents if x.outcome.passed] assert len(passevents) == 1 - -class XxxTestDirectories(object): - # need complete rewrite, and unsure if it makes sense at all - def test_simple_parse(self): - sshhosts = [HostInfo(i) for i in ['h1', 'h2', 'h3']] - parse_directories(sshhosts) - - def test_sophisticated_parse(self): - sshhosts = ['a at h1:/tmp', 'h2:tmp', 'h3'] - dirs = parse_directories(sshhosts) - assert py.builtin.sorted( - dirs.values()) == ['/tmp', 'pytestcache', 'tmp'] - - def test_parse_multiple_hosts(self): - hosts = ['h1', 'h1', 'h1:/tmp'] - dirs = parse_directories(hosts) - assert dirs == {(0, 'h1'): 'pytestcache', (1, 'h1'): 'pytestcache', - (2, 'h1'):'/tmp'} - -class TestInithosts(object): - def test_inithosts(self): - testevents = [] - hostnames = ['h1:/tmp', 'h1:/tmp', 'h1:/other', 'h2', 'h2:home'] - hosts = [HostInfo(i) for i in hostnames] - parse_directories(hosts) - config = py.test.config._reparse([]) - opts = HostOptions(do_sync=False, create_gateways=False) - hm = HostManager(hosts, config, opts) - nodes = hm.init_hosts(testevents.append) - events = [i for i in testevents if isinstance(i, report.HostRSyncing)] - assert len(events) == 4 - assert events[0].host.hostname == 'h1' - assert events[0].host.relpath == '/tmp-h1' - assert events[1].host.hostname == 'h1' - assert events[1].host.relpath == '/other-h1' - assert events[2].host.hostname == 'h2' - assert events[2].host.relpath == 'pytestcache-h2' - assert events[3].host.hostname == 'h2' - assert events[3].host.relpath == 'home-h2' def test_rsession_no_disthost(): tmpdir = py.test.ensuretemp("rsession_no_disthost") Deleted: /py/trunk/py/test/rsession/testing/test_rsync.py ============================================================================== --- /py/trunk/py/test/rsession/testing/test_rsync.py Sun Feb 4 15:05:01 2007 +++ (empty file) @@ -1,30 +0,0 @@ - -""" RSync filter test -""" - -import py -from py.__.test.rsession.hostmanage import HostRSync - -def test_rsync(): - tmpdir = py.test.ensuretemp("rsync_rsession") - tmpdir.ensure("a", dir=True) - tmpdir.ensure("b", dir=True) - tmpdir.ensure("conftest.py").write(py.code.Source(""" - dist_rsyncroots = ['a'] - """)) - tmpdir.join("a").ensure("x") - adir = tmpdir.join("a").ensure("xy", dir=True) - adir.ensure("conftest.py").write(py.code.Source(""" - dist_rsyncroots = ['b', 'conftest.py'] - """)) - adir.ensure("a", dir=True) - adir.ensure("b", dir=True) - config = py.test.config._reparse([str(tmpdir)]) - h = HostRSync(config) - h.sourcedir = config.topdir - assert h.filter(str(tmpdir.join("a"))) - assert not h.filter(str(tmpdir.join("b"))) - assert h.filter(str(tmpdir.join("a").join("x"))) - assert h.filter(str(adir.join("conftest.py"))) - assert not h.filter(str(adir.join("a"))) - assert h.filter(str(adir.join("b"))) Modified: py/trunk/py/test/rsession/testing/test_slave.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_slave.py (original) +++ py/trunk/py/test/rsession/testing/test_slave.py Sun Feb 4 15:05:01 2007 @@ -11,6 +11,7 @@ py.test.skip("rsession is unsupported on Windows.") def setup_module(module): + module.tmpdir = py.test.ensuretemp(module.__name__) module.rootdir = py.path.local(py.__file__).dirpath().dirpath() module.rootcol = py.test.collect.Directory(rootdir) @@ -107,64 +108,11 @@ assert not outcome.setupfailure assert outcome.excinfo -def test_slave_main_simple(): - res = [] - failitem = rootcol._getitembynames(funcfail_spec) - passitem = rootcol._getitembynames(funcpass_spec) - q = [None, - passitem._get_collector_trail(), - failitem._get_collector_trail() - ] - config = py.test.config._reparse([]) - pidinfo = PidInfo() - slave_main(q.pop, res.append, str(rootdir), config, pidinfo) - assert len(res) == 2 - res_repr = [ReprOutcome(r) for r in res] - assert not res_repr[0].passed and res_repr[1].passed - def test_slave_run_different_stuff(): node = gettestnode() node.run(rootcol._getitembynames("py doc log.txt".split()). _get_collector_trail()) -def test_slave_setup_exit(): - tmp = py.test.ensuretemp("slaveexit") - tmp.ensure("__init__.py") - q = py.std.Queue.Queue() - config = py.test.config._reparse([tmp]) - - class C: - res = [] - def __init__(self): - self.q = [str(tmp), - config.make_repr(conftestnames=['dist_nicelevel']), - funchang_spec, - 42, - funcpass_spec] - self.q.reverse() - - def receive(self): - return self.q.pop() - - def setcallback(self, callback): - import thread - def f(): - while 1: - callback(self.q.pop()) - f() - #thread.start_new_thread(f, ()) - - def close(self): - pass - - send = res.append - try: - exec py.code.Source(setup, "setup()").compile() in {'channel':C()} - except SystemExit: - pass - else: - py.test.fail("Did not exit") - def test_pidinfo(): if not hasattr(os, 'fork') or not hasattr(os, 'waitpid'): py.test.skip("Platform does not support fork") From guido at codespeak.net Sun Feb 4 15:27:16 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Sun, 4 Feb 2007 15:27:16 +0100 (CET) Subject: [py-svn] r37912 - in py/trunk/py: . builtin code compat execnet io log magic misc misc/testing path process test xmlobj Message-ID: <20070204142716.525311006E@code0.codespeak.net> Author: guido Date: Sun Feb 4 15:27:10 2007 New Revision: 37912 Modified: py/trunk/py/__init__.py py/trunk/py/builtin/__init__.py py/trunk/py/code/__init__.py py/trunk/py/compat/__init__.py py/trunk/py/execnet/__init__.py py/trunk/py/initpkg.py py/trunk/py/io/__init__.py py/trunk/py/log/__init__.py py/trunk/py/magic/__init__.py py/trunk/py/misc/std.py py/trunk/py/misc/testing/test_initpkg.py py/trunk/py/path/__init__.py py/trunk/py/process/__init__.py py/trunk/py/test/__init__.py py/trunk/py/xmlobj/__init__.py Log: Made some small changes to the initpkg mechanism so docstrings are, if provided in the exportdefs, copied to the namespaces (from whereever they come), and added docstrings to all exposed namespaces (except for _thread for now). Modified: py/trunk/py/__init__.py ============================================================================== --- py/trunk/py/__init__.py (original) +++ py/trunk/py/__init__.py Sun Feb 4 15:27:10 2007 @@ -22,6 +22,7 @@ exportdefs = { # helpers for use from test functions or collectors + 'test.__doc__' : ('./test/__init__.py', '__doc__'), 'test.raises' : ('./test/raises.py', 'raises'), 'test.deprecated_call' : ('./test/deprecate.py', 'deprecated_call'), 'test.skip' : ('./test/item.py', 'skip'), @@ -52,14 +53,17 @@ # hook into the top-level standard library 'std' : ('./misc/std.py', 'std'), + 'process.__doc__' : ('./process/__init__.py', '__doc__'), 'process.cmdexec' : ('./process/cmdexec.py', 'cmdexec'), - # path implementations + # path implementation + 'path.__doc__' : ('./path/__init__.py', '__doc__'), 'path.svnwc' : ('./path/svn/wccommand.py', 'SvnWCCommandPath'), 'path.svnurl' : ('./path/svn/urlcommand.py', 'SvnCommandPath'), 'path.local' : ('./path/local/local.py', 'LocalPath'), # some nice slightly magic APIs + 'magic.__doc__' : ('./magic/__init__.py', '__doc__'), 'magic.greenlet' : ('./magic/greenlet.py', 'greenlet'), 'magic.invoke' : ('./magic/invoke.py', 'invoke'), 'magic.revoke' : ('./magic/invoke.py', 'revoke'), @@ -69,6 +73,7 @@ 'magic.AssertionError' : ('./magic/assertion.py', 'AssertionError'), # python inspection/code-generation API + 'code.__doc__' : ('./code/__init__.py', '__doc__'), 'code.compile' : ('./code/source.py', 'compile_'), 'code.Source' : ('./code/source.py', 'Source'), 'code.Code' : ('./code/code.py', 'Code'), @@ -77,6 +82,7 @@ 'code.Traceback' : ('./code/traceback2.py', 'Traceback'), # backports and additions of builtins + 'builtin.__doc__' : ('./builtin/__init__.py', '__doc__'), 'builtin.enumerate' : ('./builtin/enumerate.py', 'enumerate'), 'builtin.reversed' : ('./builtin/reversed.py', 'reversed'), 'builtin.sorted' : ('./builtin/sorted.py', 'sorted'), @@ -85,6 +91,7 @@ 'builtin.frozenset' : ('./builtin/set.py', 'frozenset'), # gateways into remote contexts + 'execnet.__doc__' : ('./execnet/__init__.py', '__doc__'), 'execnet.SocketGateway' : ('./execnet/register.py', 'SocketGateway'), 'execnet.PopenGateway' : ('./execnet/register.py', 'PopenGateway'), 'execnet.SshGateway' : ('./execnet/register.py', 'SshGateway'), @@ -93,6 +100,7 @@ 'execnet.RSync' : ('./execnet/rsync.py', 'RSync'), # input-output helping + 'io.__doc__' : ('./io/__init__.py', '__doc__'), 'io.dupfile' : ('./io/dupfile.py', 'dupfile'), 'io.FDCapture' : ('./io/fdcapture.py', 'FDCapture'), 'io.StdCapture' : ('./io/stdcapture.py', 'StdCapture'), @@ -102,6 +110,7 @@ 'error' : ('./misc/error.py', 'error'), # small and mean xml/html generation + 'xml.__doc__' : ('./xmlobj/__init__.py', '__doc__'), 'xml.html' : ('./xmlobj/html.py', 'html'), 'xml.Tag' : ('./xmlobj/xml.py', 'Tag'), 'xml.raw' : ('./xmlobj/xml.py', 'raw'), @@ -109,6 +118,7 @@ 'xml.escape' : ('./xmlobj/misc.py', 'escape'), # logging API ('producers' and 'consumers' connected via keywords) + 'log.__doc__' : ('./log/__init__.py', '__doc__'), 'log.Producer' : ('./log/producer.py', 'Producer'), 'log.default' : ('./log/producer.py', 'default'), 'log._getstate' : ('./log/producer.py', '_getstate'), @@ -121,8 +131,10 @@ 'log.get' : ('./log/logger.py', 'get'), # compatibility modules (taken from 2.4.4) + 'compat.__doc__' : ('./compat/__init__.py', '__doc__'), 'compat.doctest' : ('./compat/doctest.py', '*'), 'compat.optparse' : ('./compat/optparse.py', '*'), 'compat.textwrap' : ('./compat/textwrap.py', '*'), 'compat.subprocess' : ('./compat/subprocess.py', '*'), }) + Modified: py/trunk/py/builtin/__init__.py ============================================================================== --- py/trunk/py/builtin/__init__.py (original) +++ py/trunk/py/builtin/__init__.py Sun Feb 4 15:27:10 2007 @@ -1 +1,2 @@ -# +""" backports and additions of builtins """ + Modified: py/trunk/py/code/__init__.py ============================================================================== --- py/trunk/py/code/__init__.py (original) +++ py/trunk/py/code/__init__.py Sun Feb 4 15:27:10 2007 @@ -1 +1 @@ -# +""" python inspection/code generation API """ Modified: py/trunk/py/compat/__init__.py ============================================================================== --- py/trunk/py/compat/__init__.py (original) +++ py/trunk/py/compat/__init__.py Sun Feb 4 15:27:10 2007 @@ -1 +1,2 @@ -# +""" compatibility modules (taken from 2.4.4) """ + Modified: py/trunk/py/execnet/__init__.py ============================================================================== --- py/trunk/py/execnet/__init__.py (original) +++ py/trunk/py/execnet/__init__.py Sun Feb 4 15:27:10 2007 @@ -1 +1 @@ -# +""" ad-hoc networking mechanism """ Modified: py/trunk/py/initpkg.py ============================================================================== --- py/trunk/py/initpkg.py (original) +++ py/trunk/py/initpkg.py Sun Feb 4 15:27:10 2007 @@ -259,7 +259,11 @@ "only root modules are allowed to be non-lazy. " deferred_imports.append((mod, pyparts[-1], extpy)) else: - mod.__map__[lastmodpart] = extpy + if extpy[1] == '__doc__': + mod.__doc__ = pkg._resolve(extpy) + else: + mod.__map__[lastmodpart] = extpy for mod, pypart, extpy in deferred_imports: setattr(mod, pypart, pkg._resolve(extpy)) + Modified: py/trunk/py/io/__init__.py ============================================================================== --- py/trunk/py/io/__init__.py (original) +++ py/trunk/py/io/__init__.py Sun Feb 4 15:27:10 2007 @@ -1 +1 @@ -# +""" input/output helping """ Modified: py/trunk/py/log/__init__.py ============================================================================== --- py/trunk/py/log/__init__.py (original) +++ py/trunk/py/log/__init__.py Sun Feb 4 15:27:10 2007 @@ -1 +1,2 @@ -# +""" logging API ('producers' and 'consumers' connected via keywords) """ + Modified: py/trunk/py/magic/__init__.py ============================================================================== --- py/trunk/py/magic/__init__.py (original) +++ py/trunk/py/magic/__init__.py Sun Feb 4 15:27:10 2007 @@ -1 +1 @@ -# +""" some nice, slightly magic APIs """ Modified: py/trunk/py/misc/std.py ============================================================================== --- py/trunk/py/misc/std.py (original) +++ py/trunk/py/misc/std.py Sun Feb 4 15:27:10 2007 @@ -2,6 +2,8 @@ import sys class Std(object): + """ (lazily) hook into the top-level standard library """ + def __init__(self): self.__dict__ = sys.modules Modified: py/trunk/py/misc/testing/test_initpkg.py ============================================================================== --- py/trunk/py/misc/testing/test_initpkg.py (original) +++ py/trunk/py/misc/testing/test_initpkg.py Sun Feb 4 15:27:10 2007 @@ -127,12 +127,15 @@ tfile.write(py.code.Source(""" import py py.initpkg('realtest', { + 'x.module.__doc__': ('./testmodule.py', '__doc__'), 'x.module': ('./testmodule.py', '*'), }) """)) tfile = pkgdir.join('testmodule.py') tfile.write(py.code.Source(""" + 'test module' + __all__ = ['mytest0', 'mytest1', 'MyTest'] def mytest0(): @@ -186,6 +189,11 @@ assert 'mytest1' in moddict assert 'MyTest' in moddict + def test_realmodule___doc__(self): + """test whether the __doc__ attribute is set properly from initpkg""" + import realtest.x.module + assert realtest.x.module.__doc__ == 'test module' + #class TestStdHook: # """Tests imports for the standard Python library hook.""" # Modified: py/trunk/py/path/__init__.py ============================================================================== --- py/trunk/py/path/__init__.py (original) +++ py/trunk/py/path/__init__.py Sun Feb 4 15:27:10 2007 @@ -1 +1 @@ -# +""" unified file system api """ Modified: py/trunk/py/process/__init__.py ============================================================================== --- py/trunk/py/process/__init__.py (original) +++ py/trunk/py/process/__init__.py Sun Feb 4 15:27:10 2007 @@ -1 +1 @@ -# +""" high-level sub-process handling """ Modified: py/trunk/py/test/__init__.py ============================================================================== --- py/trunk/py/test/__init__.py (original) +++ py/trunk/py/test/__init__.py Sun Feb 4 15:27:10 2007 @@ -1 +1 @@ -# +""" versatile unit-testing tool + libraries """ Modified: py/trunk/py/xmlobj/__init__.py ============================================================================== --- py/trunk/py/xmlobj/__init__.py (original) +++ py/trunk/py/xmlobj/__init__.py Sun Feb 4 15:27:10 2007 @@ -1 +1,2 @@ -# +""" small and mean xml/html generation """ + From guido at codespeak.net Sun Feb 4 15:35:30 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Sun, 4 Feb 2007 15:35:30 +0100 (CET) Subject: [py-svn] r37913 - in py/trunk/py/apigen: . testing Message-ID: <20070204143530.ACB401006E@code0.codespeak.net> Author: guido Date: Sun Feb 4 15:35:28 2007 New Revision: 37913 Modified: py/trunk/py/apigen/apigen.py py/trunk/py/apigen/html.py py/trunk/py/apigen/htmlgen.py py/trunk/py/apigen/testing/test_apigen_example.py py/trunk/py/apigen/testing/test_apigen_functional.py py/trunk/py/apigen/todo-apigen.txt Log: Some more cleanups in HTML generation, fixed support for docstrings in namespaces, in order to do this I had to change the way objects are retrieved: instead of getting them from the DSA I now walk the package tree, small change in apigen.py: to allow re-using the get_documentable_items function I split it up in a generic and a specific part. Modified: py/trunk/py/apigen/apigen.py ============================================================================== --- py/trunk/py/apigen/apigen.py (original) +++ py/trunk/py/apigen/apigen.py Sun Feb 4 15:35:28 2007 @@ -12,13 +12,23 @@ from py.__.apigen import project from py.__.apigen.tracer.docstorage import pkg_to_dict -def get_documentable_items(pkgdir): +def get_documentable_items_pkgdir(pkgdir): + """ get all documentable items from an initpkg pkgdir + + this is a generic implementation, import as 'get_documentable_items' + from your module when using initpkg to get all public stuff in the + package documented + """ sys.path.insert(0, str(pkgdir.dirpath())) rootmod = __import__(pkgdir.basename) d = pkg_to_dict(rootmod) + return pkgdir.basename, d + +def get_documentable_items(pkgdir): + pkgname, pkgdict = get_documentable_items_pkgdir(pkgdir) from py.__.execnet.channel import Channel - #d['execnet.Channel'] = Channel # XXX doesn't work - return 'py', d + # pkgdict['execnet.Channel'] = Channel # XXX doesn't work + return pkgname, pkgdict def build(pkgdir, dsa, capture): l = linker.Linker() Modified: py/trunk/py/apigen/html.py ============================================================================== --- py/trunk/py/apigen/html.py (original) +++ py/trunk/py/apigen/html.py Sun Feb 4 15:35:28 2007 @@ -82,16 +82,25 @@ link, H.div(class_='code', *sourceels)) class SourceDef(html.div): - pass + def __init__(self, *sourceels): + super(H.SourceDef, self).__init__( + H.div(class_='code', *sourceels)) class NonPythonSource(html.pre): pass # style = html.Style(margin_left='15em') class DirList(html.div): - pass # style = html.Style(margin_left='15em') + def __init__(self, dirs, files): + dirs = [H.DirListItem(text, href) for (text, href) in dirs] + files = [H.DirListItem(text, href) for (text, href) in files] + super(H.DirList, self).__init__( + H.h2('directories'), dirs, + H.h2('files'), files, + ) class DirListItem(html.div): - pass + def __init__(self, text, href): + super(H.DirListItem, self).__init__(H.a(text, href=href)) class ValueDescList(html.ul): def __init__(self, *args, **kwargs): Modified: py/trunk/py/apigen/htmlgen.py ============================================================================== --- py/trunk/py/apigen/htmlgen.py (original) +++ py/trunk/py/apigen/htmlgen.py Sun Feb 4 15:35:28 2007 @@ -14,6 +14,9 @@ html = py.xml.html raw = py.xml.raw +def is_navigateable(name): + return (not is_private(name) and name != '__doc__') + def deindent(str, linesep='\n'): """ de-indent string @@ -46,6 +49,16 @@ ret.append(line[deindent:]) return '%s\n' % (linesep.join(ret),) +def get_linesep(s, default='\n'): + """ return the line seperator of a string + + returns 'default' if no seperator can be found + """ + for sep in ('\r\n', '\r', '\n'): + if sep in s: + return sep + return default + def get_param_htmldesc(linker, func): """ get the html for the parameters of a function """ import inspect @@ -161,27 +174,21 @@ re = py.std.re _reg_body = re.compile(r']*>(.*)', re.S) def build_python_page(self, fspath): - mod = source_browser.parse_path(fspath) - # XXX let's cheat a bit here... there should be a different function - # using the linker, and returning a proper py.xml.html element, - # at some point - html = source_html.create_html(mod) - snippet = self._reg_body.search(html).group(1) - tag = H.SourceDef(raw(snippet)) + # XXX two reads of the same file here... not very bad (disk caches + # and such) but also not very nice... + enc = source_html.get_module_encoding(fspath.strpath) + source = fspath.read() + sep = get_linesep(source) + colored = enumerate_and_color(source.split(sep), 0, enc) + tag = H.SourceDef(*colored) nav = self.build_navigation(fspath) return tag, nav def build_dir_page(self, fspath): - tag = H.DirList() dirs, files = source_dirs_files(fspath) - tag.append(H.h2('directories')) - for path in dirs: - tag.append(H.DirListItem(H.a(path.basename, - href=self.linker.get_lazyhref(str(path))))) - tag.append(H.h2('files')) - for path in files: - tag.append(H.DirListItem(H.a(path.basename, - href=self.linker.get_lazyhref(str(path))))) + dirs = [(p.basename, self.linker.get_lazyhref(str(p))) for p in dirs] + files = [(p.basename, self.linker.get_lazyhref(str(p))) for p in files] + tag = H.DirList(dirs, files) nav = self.build_navigation(fspath) return tag, nav @@ -238,7 +245,8 @@ else: tag, nav = self.build_nonpython_page(fspath) title = 'sources for %s' % (fspath.basename,) - reltargetpath = outputpath.relto(self.base).replace(os.path.sep, '/') + reltargetpath = outputpath.relto(self.base).replace(os.path.sep, + '/') self.write_page(title, reltargetpath, project, tag, nav) def enumerate_and_color(codelines, firstlineno, enc): @@ -255,6 +263,20 @@ break return colored +def get_obj(pkg, dotted_name): + full_dotted_name = '%s.%s' % (pkg.__name__, dotted_name) + if dotted_name == '': + return pkg + path = dotted_name.split('.') + ret = pkg + for item in path: + marker = [] + ret = getattr(ret, item, marker) + if ret is marker: + raise NameError('can not access %s in %s' % (item, + full_dotted_name)) + return ret + class ApiPageBuilder(AbstractPageBuilder): """ builds the html for an api docs page """ def __init__(self, base, linker, dsa, projroot, namespace_tree, @@ -267,10 +289,13 @@ self.namespace_tree = namespace_tree self.capture = capture + pkgname = self.dsa.get_module_name().split('/')[-1] + self.pkg = __import__(pkgname) + def build_callable_view(self, dotted_name): """ build the html for a class method """ # XXX we may want to have seperate - func = self.dsa.get_obj(dotted_name) + func = get_obj(self.pkg, dotted_name) docstring = func.__doc__ if docstring: docstring = deindent(docstring) @@ -289,7 +314,8 @@ enc = source_html.get_module_encoding(sourcefile) tokenizer = source_color.Tokenizer(source_color.PythonSchema) firstlineno = func.func_code.co_firstlineno - org = callable_source.split('\n') + sep = get_linesep(callable_source) + org = callable_source.split(sep) colored = enumerate_and_color(org, firstlineno, enc) text = 'source: %s' % (sourcefile,) if is_in_pkg: @@ -307,7 +333,7 @@ def build_class_view(self, dotted_name): """ build the html for a class """ - cls = self.dsa.get_obj(dotted_name) + cls = get_obj(self.pkg, dotted_name) # XXX is this a safe check? try: sourcefile = inspect.getsourcefile(cls) @@ -360,21 +386,15 @@ def build_namespace_view(self, namespace_dotted_name, item_dotted_names): """ build the html for a namespace (module) """ - try: - obj = self.dsa.get_obj(namespace_dotted_name) - except KeyError: - docstring = None - else: - docstring = obj.__doc__ - if docstring: - docstring = deindent(docstring) + obj = get_obj(self.pkg, namespace_dotted_name) + docstring = obj.__doc__ snippet = H.NamespaceDescription( H.NamespaceDef(namespace_dotted_name), H.Docstring(docstring or '*no docstring available*') ) for dotted_name in sorted(item_dotted_names): itemname = dotted_name.split('.')[-1] - if is_private(itemname): + if not is_navigateable(itemname): continue snippet.append( H.NamespaceItem( @@ -490,7 +510,7 @@ selected = dn == '.'.join(path) sibpath = dn.split('.') sibname = sibpath[-1] - if is_private(sibname): + if not is_navigateable(sibname): continue navitems.append(H.NavigationItem(self.linker, dn, sibname, depth, selected)) @@ -560,7 +580,6 @@ return py.path.local(sourcefile).relto(self.projpath) def build_callsite(self, functionname, call_site): - print 'building callsite for', functionname tbtag = self.gen_traceback(functionname, reversed(call_site)) return H.CallStackItem(call_site[0].filename, call_site[0].lineno + 1, tbtag) @@ -575,7 +594,10 @@ tokenizer = source_color.Tokenizer(source_color.PythonSchema) mangled = [] - for i, sline in enumerate(str(source).split('\n')): + + source = str(source) + sep = get_linesep(source) + for i, sline in enumerate(source.split(sep)): if i == lineno: l = '-> %s' % (sline,) else: Modified: py/trunk/py/apigen/testing/test_apigen_example.py ============================================================================== --- py/trunk/py/apigen/testing/test_apigen_example.py (original) +++ py/trunk/py/apigen/testing/test_apigen_example.py Sun Feb 4 15:35:28 2007 @@ -116,7 +116,7 @@ self.namespace_tree = namespace_tree self.apb = ApiPageBuilder(base, linker, self.dsa, self.fs_root.join(self.pkg_name), - namespace_tree) + namespace_tree, 'root docstring') self.spb = SourcePageBuilder(base, linker, self.fs_root.join(self.pkg_name)) @@ -130,7 +130,7 @@ pkg.main.sub.func(pkg.main.SomeClass(10)) t.end_tracing() apb = ApiPageBuilder(self.base, self.linker, dsa, self.fs_root, - self.namespace_tree) + self.namespace_tree, 'root docstring') snippet = apb.build_callable_view('main.sub.func') html = snippet.unicode() print html @@ -371,8 +371,7 @@ assert funcsource.check(file=True) html = funcsource.read() print html - assert ('def ' - 'func(arg1):') in html + assert ('def func(arg1)') in html def test_build_navigation_root(self): self.spb.prepare_pages(self.fs_root) Modified: py/trunk/py/apigen/testing/test_apigen_functional.py ============================================================================== --- py/trunk/py/apigen/testing/test_apigen_functional.py (original) +++ py/trunk/py/apigen/testing/test_apigen_functional.py Sun Feb 4 15:35:28 2007 @@ -42,15 +42,24 @@ return 'quux' """)) temp.ensure("pak/__init__.py").write(py.code.Source("""\ + '''pkg docstring''' from py.initpkg import initpkg - initpkg(__name__, exportdefs = { - 'main.sub.func': ("./func.py", "func"), - 'main.func': ("./func.py", "func_2"), - 'main.SomeTestClass': ('./sometestclass.py', 'SomeTestClass'), - 'main.SomeTestSubClass': ('./sometestsubclass.py', - 'SomeTestSubClass'), - 'somenamespace': ('./somenamespace.py', '*'), - }) + initpkg(__name__, + long_description=globals()['__doc__'], + exportdefs={'main.sub.func': ("./func.py", "func"), + 'main.func': ("./func.py", "func_2"), + 'main.SomeTestClass': ('./sometestclass.py', + 'SomeTestClass'), + 'main.SomeTestSubClass': ('./sometestsubclass.py', + 'SomeTestSubClass'), + 'somenamespace': ('./somenamespace.py', '*')}) + """)) + temp.ensure('apigen.py').write(py.code.Source("""\ + import py + py.std.sys.path.insert(0, + py.magic.autopath().dirpath().dirpath().dirpath().strpath) + from py.__.apigen.apigen import build, \ + get_documentable_items_pkgdir as get_documentable_items """)) temp.ensure('pak/test/test_pak.py').write(py.code.Source("""\ import py @@ -87,8 +96,8 @@ def test_get_documentable_items(): fs_root, package_name = setup_fs_project('test_get_documentable_items') pkgname, documentable = apigen.get_documentable_items( - fs_root.join(package_name)) - assert pkgname == 'py' + fs_root.join(package_name)) + assert pkgname == 'pak' assert sorted(documentable.keys()) == [ 'main.SomeTestClass', 'main.SomeTestSubClass', 'main.func', 'main.sub.func', 'somenamespace.baz', 'somenamespace.foo'] @@ -101,14 +110,14 @@ pydir = py.magic.autopath().dirpath().dirpath().dirpath() pakdir = fs_root.join('pak') if py.std.sys.platform == 'win32': - cmd = 'set APIGEN_TARGET=%s && python "%s/bin/py.test"' % (tempdir, - pydir) + cmd = ('set APIGEN_TARGET=%s && set PYTHONPATH=%s && ' + 'python "%s/bin/py.test"') % (tempdir, fs_root, pydir) else: - cmd = 'APIGEN_TARGET="%s" "%s/bin/py.test"' % (tempdir, pydir) + cmd = ('APIGEN_TARGET="%s" PYTHONPATH="%s" ' + '"%s/bin/py.test"') % (tempdir, fs_root, pydir) try: output = py.process.cmdexec('%s --apigen="%s/apigen.py" "%s"' % ( - cmd, pydir.join('apigen'), - pakdir)) + cmd, fs_root, pakdir)) except py.error.Error, e: print e.out raise @@ -130,6 +139,10 @@ assert namespace_api.check(file=True) html = namespace_api.read() assert 'SomeTestClass' in html + index = apidir.join('index.html') + assert index.check(file=True) + html = index.read() + assert 'pkg docstring' in html sourcedir = tempdir.join('source') assert sourcedir.check(dir=True) Modified: py/trunk/py/apigen/todo-apigen.txt ============================================================================== --- py/trunk/py/apigen/todo-apigen.txt (original) +++ py/trunk/py/apigen/todo-apigen.txt Sun Feb 4 15:35:28 2007 @@ -10,7 +10,7 @@ viewed. method views (when navigating there through the class view) should also have the source there - DONE I guess (todo: add syntax coloring) + DONE I think * have class-level attributes be displayed From hpk at codespeak.net Sun Feb 4 15:36:44 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 4 Feb 2007 15:36:44 +0100 (CET) Subject: [py-svn] r37914 - py/trunk/py/test/rsession/testing Message-ID: <20070204143644.706B91006E@code0.codespeak.net> Author: hpk Date: Sun Feb 4 15:36:42 2007 New Revision: 37914 Modified: py/trunk/py/test/rsession/testing/test_rsession.py Log: cleaner test setup Modified: py/trunk/py/test/rsession/testing/test_rsession.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_rsession.py (original) +++ py/trunk/py/test/rsession/testing/test_rsession.py Sun Feb 4 15:36:42 2007 @@ -10,28 +10,10 @@ funcpass_spec, funcskip_spec, funcprint_spec, funcprintfail_spec, \ funcoptioncustom_spec +from test_hostmanage import DirSetup + def setup_module(mod): mod.pkgdir = py.path.local(py.__file__).dirpath() - mod.tmpdir = py.test.ensuretemp(mod.__name__) - - -#def test_make_colitems(): -# one = pkgdir.join("initpkg.py") -# two = pkgdir.join("path", "__init__.py")# - -# cols = RSession.make_colitems([one, two], baseon=pkgdir) -# assert len(cols) == 2 -# col_one, col_two = cols -# assert col_one.listnames() == ["py", "initpkg.py"] -# assert col_two.listnames() == ["py", "path", "__init__.py"]# -# -# cols = RSession.make_colitems([one, two], baseon=pkgdir.dirpath()) -# assert len(cols) == 2 -# col_one, col_two = cols -# assert col_one.listnames() == [pkgdir.dirpath().basename, -# "py", "initpkg.py"] -# assert col_two.listnames() == [pkgdir.dirpath().basename, -# "py", "path", "__init__.py"] def test_example_tryiter(): events = [] @@ -50,15 +32,13 @@ assert len(events) == 2 assert str(events[1][0].value) == "Reason" -class TestRSessionRemote: +class TestRSessionRemote(DirSetup): def test_example_distribution_minus_x(self): - destdir = py.test.ensuretemp("example_dist_dest_x") - tmpdir = py.test.ensuretemp("example_distribution_minus_x") - tmpdir.ensure("sub", "conftest.py").write(py.code.Source(""" + self.source.ensure("sub", "conftest.py").write(py.code.Source(""" dist_hosts = ['localhost:%s'] - """ % destdir)) - tmpdir.ensure("sub", "__init__.py") - tmpdir.ensure("sub", "test_one.py").write(py.code.Source(""" + """ % self.dest)) + self.source.ensure("sub", "__init__.py") + self.source.ensure("sub", "test_one.py").write(py.code.Source(""" def test_1(): pass def test_x(): @@ -71,7 +51,7 @@ def test_4(someargs): pass """)) - config = py.test.config._reparse([tmpdir.join("sub"), '-x']) + config = py.test.config._reparse([self.source.join("sub"), '-x']) rsession = RSession(config) allevents = [] rsession.main(reporter=allevents.append) @@ -139,7 +119,7 @@ hosts = [HostInfo('localhost')] setup_events = [] teardown_events = [] - + tmpdir = py.test.ensuretemp("emptyconftest") config = py.test.config._reparse([tmpdir]) hm = HostManager(hosts, config) nodes = hm.init_hosts(setup_events.append) From hpk at codespeak.net Sun Feb 4 15:40:48 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 4 Feb 2007 15:40:48 +0100 (CET) Subject: [py-svn] r37915 - in py/trunk/py/execnet: . testing Message-ID: <20070204144048.0BA8F10070@code0.codespeak.net> Author: hpk Date: Sun Feb 4 15:40:46 2007 New Revision: 37915 Modified: py/trunk/py/execnet/rsync.py py/trunk/py/execnet/testing/test_rsync.py Log: sanity checks Modified: py/trunk/py/execnet/rsync.py ============================================================================== --- py/trunk/py/execnet/rsync.py (original) +++ py/trunk/py/execnet/rsync.py Sun Feb 4 15:40:46 2007 @@ -19,6 +19,7 @@ for name in options: assert name in ('delete') self._options = options + assert callback is None or callable(callback) self._callback = callback self._channels = {} self._receivequeue = Queue() @@ -31,6 +32,7 @@ """ Adds a remote target specified via a 'gateway' and a remote destination directory. """ + assert finishedcallback is None or callable(finishedcallback) def itemcallback(req): self._receivequeue.put((channel, req)) channel = gateway.remote_exec(REMOTE_SOURCE) Modified: py/trunk/py/execnet/testing/test_rsync.py ============================================================================== --- py/trunk/py/execnet/testing/test_rsync.py (original) +++ py/trunk/py/execnet/testing/test_rsync.py Sun Feb 4 15:40:46 2007 @@ -87,6 +87,7 @@ class DRsync(RSync): def filter(self, x): + assert x != source if x.endswith("ex2"): self.x = 1 source.join("ex2").remove() From hpk at codespeak.net Sun Feb 4 15:42:51 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 4 Feb 2007 15:42:51 +0100 (CET) Subject: [py-svn] r37916 - py/trunk/py Message-ID: <20070204144251.CD10A1006E@code0.codespeak.net> Author: hpk Date: Sun Feb 4 15:42:50 2007 New Revision: 37916 Modified: py/trunk/py/conftest.py Log: dist_rsync_roots: py lib itself only needs itself Modified: py/trunk/py/conftest.py ============================================================================== --- py/trunk/py/conftest.py (original) +++ py/trunk/py/conftest.py Sun Feb 4 15:42:50 2007 @@ -25,3 +25,5 @@ help=("target to run tests requiring ssh, e.g. " "user at codespeak.net")), ) + +dist_rsync_roots = ['.'] From hpk at codespeak.net Sun Feb 4 16:42:56 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 4 Feb 2007 16:42:56 +0100 (CET) Subject: [py-svn] r37919 - py/trunk/py/test/rsession/testing Message-ID: <20070204154256.4CCFE10070@code0.codespeak.net> Author: hpk Date: Sun Feb 4 16:42:55 2007 New Revision: 37919 Modified: py/trunk/py/test/rsession/testing/test_master.py Log: use a less ambigous test package name Modified: py/trunk/py/test/rsession/testing/test_master.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_master.py (original) +++ py/trunk/py/test/rsession/testing/test_master.py Sun Feb 4 16:42:55 2007 @@ -96,7 +96,7 @@ class TestSlave: def setup_class(cls): cls.tmpdir = tmpdir = py.test.ensuretemp(cls.__name__) - pkgpath = tmpdir.join("pkg") + cls.pkgpath = pkgpath = tmpdir.join("slavetestpkg") pkgpath.ensure("__init__.py") pkgpath.join("test_something.py").write(py.code.Source(""" def funcpass(): @@ -114,10 +114,11 @@ return self.config.get_collector_trail(item) def test_slave_setup(self): + pkgname = self.pkgpath.basename host = HostInfo("localhost:%s" %(self.tmpdir,)) host.initgateway() channel = setup_slave(host, self.config) - spec = self._gettrail("pkg", "test_something.py", "funcpass") + spec = self._gettrail(pkgname, "test_something.py", "funcpass") print "sending", spec channel.send(spec) output = ReprOutcome(channel.receive()) From guido at codespeak.net Sun Feb 4 16:47:35 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Sun, 4 Feb 2007 16:47:35 +0100 (CET) Subject: [py-svn] r37922 - in py/trunk/py/apigen: . testing Message-ID: <20070204154735.6EC7110070@code0.codespeak.net> Author: guido Date: Sun Feb 4 16:47:33 2007 New Revision: 37922 Modified: py/trunk/py/apigen/html.py py/trunk/py/apigen/htmlgen.py py/trunk/py/apigen/layout.py py/trunk/py/apigen/style.css py/trunk/py/apigen/testing/test_apigen_functional.py py/trunk/py/apigen/testing/test_htmlgen.py py/trunk/py/apigen/todo-apigen.txt Log: Made that properties (class attributes) are displayed, fixed the links in the menubar. Modified: py/trunk/py/apigen/html.py ============================================================================== --- py/trunk/py/apigen/html.py (original) +++ py/trunk/py/apigen/html.py Sun Feb 4 16:47:33 2007 @@ -47,12 +47,21 @@ super(H.FunctionInfo, self).__init__( H.Hideable('funcinfo', 'funcinfo', valuedesc, csource, callstack)) + + class PropertyDescription(html.div): + def __init__(self, name, value): + if type(value) not in [str, unicode]: + value = str(value) + if len(value) > 100: + value = value[:100] + '...' + super(H.PropertyDescription, self).__init__(H.strong(name), ': ', + H.em(value), + class_='property') class ParameterDescription(html.div): pass class Docstring(html.pre): - style = html.Style(width='100%') pass class Navigation(html.div): Modified: py/trunk/py/apigen/htmlgen.py ============================================================================== --- py/trunk/py/apigen/htmlgen.py (original) +++ py/trunk/py/apigen/htmlgen.py Sun Feb 4 16:47:33 2007 @@ -17,6 +17,17 @@ def is_navigateable(name): return (not is_private(name) and name != '__doc__') +def show_property(name): + if not name.startswith('_'): + return True + if name.startswith('__') and name.endswith('__'): + # XXX do we need to skip more manually here? + if (name not in dir(object) and + name not in ['__doc__', '__dict__', '__name__', '__module__', + '__weakref__']): + return True + return False + def deindent(str, linesep='\n'): """ de-indent string @@ -351,7 +362,24 @@ docstring = cls.__doc__ if docstring: docstring = deindent(docstring) - methods = self.dsa.get_class_methods(dotted_name) + if not hasattr(cls, '__name__'): + clsname = 'instance of %s' % (cls.__class__.__name__,) + else: + clsname = cls.__name__ + bases = self.build_bases(dotted_name) + properties = self.build_properties(cls) + methods = self.build_methods(dotted_name) + snippet = H.ClassDescription( + # XXX bases HTML + H.ClassDef('%s(' % (clsname,), *bases), + H.Docstring(docstring or '*no docstring available*'), + sourcelink, + *(properties+methods) + ) + + return snippet + + def build_bases(self, dotted_name): basehtml = [] bases = self.dsa.get_possible_base_classes(dotted_name) for base in bases: @@ -366,23 +394,33 @@ if basehtml: basehtml.pop() basehtml.append('):') - if not hasattr(cls, '__name__'): - clsname = 'instance of %s' % (cls.__class__.__name__,) - else: - clsname = cls.__name__ - snippet = H.ClassDescription( - # XXX bases HTML - H.ClassDef('%s(' % (clsname,), *basehtml), - H.Docstring(docstring or '*no docstring available*'), - sourcelink, - ) + return basehtml + + def build_properties(self, cls): + properties = [] + for attr in dir(cls): + val = getattr(cls, attr) + if show_property(attr) and not callable(val): + if isinstance(val, property): + val = '' + properties.append((attr, val)) + properties.sort(key=lambda a: a[0]) # sort on name + ret = [] + if properties: + ret.append(H.h2('properties:')) + for name, val in properties: + ret.append(H.PropertyDescription(name, val)) + return ret + + def build_methods(self, dotted_name): + ret = [] + methods = self.dsa.get_class_methods(dotted_name) if methods: - snippet.append(H.h2('methods:')) + ret.append(H.h2('methods:')) for method in methods: - snippet += self.build_callable_view('%s.%s' % (dotted_name, - method)) - # XXX properties - return snippet + ret += self.build_callable_view('%s.%s' % (dotted_name, + method)) + return ret def build_namespace_view(self, namespace_dotted_name, item_dotted_names): """ build the html for a namespace (module) """ Modified: py/trunk/py/apigen/layout.py ============================================================================== --- py/trunk/py/apigen/layout.py (original) +++ py/trunk/py/apigen/layout.py Sun Feb 4 16:47:33 2007 @@ -26,9 +26,17 @@ def fill(self): super(LayoutPage, self).fill() - #self.menubar[:] = [] + self.update_menubar_links(self.menubar) self.body.insert(0, self.nav) + def update_menubar_links(self, node): + for item in node: + if not isinstance(item, py.xml.Tag): + continue + if (item.__class__.__name__ == 'a' and hasattr(item.attr, 'href') + and not item.attr.href.startswith('http://')): + item.attr.href = self.relpath + '../py/doc/' + item.attr.href + def setup_scripts_styles(self, copyto=None): for path, name in self.stylesheets: if copyto: Modified: py/trunk/py/apigen/style.css ============================================================================== --- py/trunk/py/apigen/style.css (original) +++ py/trunk/py/apigen/style.css Sun Feb 4 16:47:33 2007 @@ -18,7 +18,6 @@ #content { border: 0px; height: 95%; - width: 100%; } ul { @@ -76,6 +75,10 @@ background-color: white; } +.property { + font-size: 1.2em; +} + .callstackitem { border: 1px solid black; margin-bottom: 1em; Modified: py/trunk/py/apigen/testing/test_apigen_functional.py ============================================================================== --- py/trunk/py/apigen/testing/test_apigen_functional.py (original) +++ py/trunk/py/apigen/testing/test_apigen_functional.py Sun Feb 4 16:47:33 2007 @@ -19,6 +19,7 @@ temp.ensure('pak/sometestclass.py').write(py.code.Source("""\ class SomeTestClass(object): " docstring sometestclass " + someattr = 'somevalue' def __init__(self, somevar): self.somevar = somevar @@ -129,12 +130,10 @@ sometestclass_api = apidir.join('main.SomeTestClass.html') assert sometestclass_api.check(file=True) html = sometestclass_api.read() + print html assert 'SomeTestClass' in html - # XXX not linking to method files anymore - #sometestclass_init_api = apidir.join('main.SomeTestClass.__init__.html') - #assert sometestclass_init_api.check(file=True) - #assert sometestclass_init_api.read().find( - # '__init__') > -1 + assert 'someattr: somevalue' in html + namespace_api = apidir.join('main.html') assert namespace_api.check(file=True) html = namespace_api.read() @@ -156,4 +155,5 @@ html = index.read() print html assert 'test' in html + assert 'href="../../py/doc/home.html"' Modified: py/trunk/py/apigen/testing/test_htmlgen.py ============================================================================== --- py/trunk/py/apigen/testing/test_htmlgen.py (original) +++ py/trunk/py/apigen/testing/test_htmlgen.py Sun Feb 4 16:47:33 2007 @@ -55,3 +55,12 @@ ' "bar"\n' '') +def test_show_property(): + assert htmlgen.show_property('foo') + assert not htmlgen.show_property('_foo') + assert htmlgen.show_property('__foo__') + assert not htmlgen.show_property('__doc__') + assert not htmlgen.show_property('__dict__') + assert not htmlgen.show_property('__name__') + assert not htmlgen.show_property('__class__') + Modified: py/trunk/py/apigen/todo-apigen.txt ============================================================================== --- py/trunk/py/apigen/todo-apigen.txt (original) +++ py/trunk/py/apigen/todo-apigen.txt Sun Feb 4 16:47:33 2007 @@ -1,5 +1,7 @@ -* format docstrings more nicely (with tests) - DONE I guess +* format docstrings more nicely (with tests) + + DONE I guess * have the API function view be as informative as possible without having to go to the "single method" view @@ -10,10 +12,12 @@ viewed. method views (when navigating there through the class view) should also have the source there - DONE I think + DONE I think, single method view is gone * have class-level attributes be displayed + DONE + * use "inherited" doc strings, i.e. for class A: def meth(self): @@ -25,8 +29,12 @@ B.meth should display the A.meth docstring, probably with special formatting (italics or so). + NOT YET DONE (later?) + * factor out some common code in the build_* functions + (mostly) DONE + * refactor the apigen/rsession interaction to become cleaner (e.g. apigen's get_documentable_items should be separately tested and the caller should not need @@ -76,9 +84,15 @@ * add syntax coloring for Python source snippets + DONE + * remove py.test/apigen cruft from stack traces + DONE, thanks to fijal + * fix non-ascii source encoding support + DONE + * XXX From guido at codespeak.net Sun Feb 4 22:11:45 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Sun, 4 Feb 2007 22:11:45 +0100 (CET) Subject: [py-svn] r37926 - py/trunk/py/apigen Message-ID: <20070204211145.8F19A10076@code0.codespeak.net> Author: guido Date: Sun Feb 4 22:11:43 2007 New Revision: 37926 Modified: py/trunk/py/apigen/html.py Log: Small visual tweaks. Modified: py/trunk/py/apigen/html.py ============================================================================== --- py/trunk/py/apigen/html.py (original) +++ py/trunk/py/apigen/html.py Sun Feb 4 22:11:43 2007 @@ -45,7 +45,7 @@ class FunctionInfo(html.div): def __init__(self, valuedesc, csource, callstack): super(H.FunctionInfo, self).__init__( - H.Hideable('funcinfo', 'funcinfo', valuedesc, csource, + H.Hideable('funcinfo', 'funcinfo', valuedesc, H.br(), csource, callstack)) class PropertyDescription(html.div): @@ -54,9 +54,9 @@ value = str(value) if len(value) > 100: value = value[:100] + '...' - super(H.PropertyDescription, self).__init__(H.strong(name), ': ', - H.em(value), - class_='property') + super(H.PropertyDescription, self).__init__(name, ': ', + H.em(value), + class_='property') class ParameterDescription(html.div): pass From guido at codespeak.net Sun Feb 4 22:11:58 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Sun, 4 Feb 2007 22:11:58 +0100 (CET) Subject: [py-svn] r37927 - in py/trunk/py/path: . svn Message-ID: <20070204211158.D197210079@code0.codespeak.net> Author: guido Date: Sun Feb 4 22:11:56 2007 New Revision: 37927 Modified: py/trunk/py/path/common.py py/trunk/py/path/svn/wccommand.py Log: Some docstrings. Modified: py/trunk/py/path/common.py ============================================================================== --- py/trunk/py/path/common.py (original) +++ py/trunk/py/path/common.py Sun Feb 4 22:11:56 2007 @@ -93,6 +93,18 @@ Checkers = Checkers def check(self, **kw): + """ check a path for existence, or query its properties + + without arguments, this returns True if the path exists (on the + filesystem), False if not + + with (keyword only) arguments, the object compares the value + of the argument with the value of a property with the same name + (if it has one, else it raises a TypeError) + + when for example the keyword argument 'ext' is '.py', this will + return True if self.ext == '.py', False otherwise + """ if kw: kw = kw.copy() if not checktype(self, kw): @@ -177,6 +189,18 @@ return repr(str(self)) def visit(self, fil=None, rec=None, ignore=_dummyclass): + """ yields all paths below the current one + + fil is a filter (glob pattern or callable), if not matching the + path will not be yielded, defaulting to None (everything is + returned) + + rec is a filter (glob pattern or callable) that controls whether + a node is descended, defaulting to None + + ignore is an Exception class that is ignoredwhen calling dirlist() + on any of the paths (by default, all exceptions are reported) + """ if isinstance(fil, str): fil = fnmatch(fil) if rec: Modified: py/trunk/py/path/svn/wccommand.py ============================================================================== --- py/trunk/py/path/svn/wccommand.py (original) +++ py/trunk/py/path/svn/wccommand.py Sun Feb 4 22:11:56 2007 @@ -55,6 +55,7 @@ return svncommon._escape_helper(cmd) def dump(self, obj): + """ pickle object into path location""" return self.localpath.dump(obj) def svnurl(self): @@ -162,6 +163,10 @@ error_enhance(sys.exc_info()) def mkdir(self, *args): + """ create & return the directory joined with args. """ + p = self.join(*args) + self._callex(os.mkdir, str(p)) + return p if args: return self.join(*args).mkdir() else: @@ -169,6 +174,7 @@ return self def add(self): + """ add ourself to svn """ self._svn('add') def remove(self, rec=1, force=1): From guido at codespeak.net Sun Feb 4 22:21:38 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Sun, 4 Feb 2007 22:21:38 +0100 (CET) Subject: [py-svn] r37928 - py/trunk/py/apigen/testing Message-ID: <20070204212138.AC4E010076@code0.codespeak.net> Author: guido Date: Sun Feb 4 22:21:35 2007 New Revision: 37928 Modified: py/trunk/py/apigen/testing/test_apigen_functional.py Log: Updated test (oops). Modified: py/trunk/py/apigen/testing/test_apigen_functional.py ============================================================================== --- py/trunk/py/apigen/testing/test_apigen_functional.py (original) +++ py/trunk/py/apigen/testing/test_apigen_functional.py Sun Feb 4 22:21:35 2007 @@ -132,7 +132,7 @@ html = sometestclass_api.read() print html assert 'SomeTestClass' in html - assert 'someattr: somevalue' in html + assert 'someattr: somevalue' in html namespace_api = apidir.join('main.html') assert namespace_api.check(file=True) From guido at codespeak.net Sun Feb 4 22:47:04 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Sun, 4 Feb 2007 22:47:04 +0100 (CET) Subject: [py-svn] r37930 - py/trunk/py/log Message-ID: <20070204214704.4193510069@code0.codespeak.net> Author: guido Date: Sun Feb 4 22:47:03 2007 New Revision: 37930 Modified: py/trunk/py/log/consumer.py py/trunk/py/log/logger.py py/trunk/py/log/producer.py Log: Docstrings. Modified: py/trunk/py/log/consumer.py ============================================================================== --- py/trunk/py/log/consumer.py (original) +++ py/trunk/py/log/consumer.py Sun Feb 4 22:47:03 2007 @@ -2,15 +2,20 @@ import sys class File(object): + """ log consumer wrapping a file(-like) object + """ def __init__(self, f): assert hasattr(f, 'write') assert isinstance(f, file) or not hasattr(f, 'open') self._file = f def __call__(self, msg): + """ write a message to the log """ print >>self._file, str(msg) class Path(object): + """ log consumer able to write log messages into + """ def __init__(self, filename, append=False, delayed_create=False, buffering=1): self._append = append @@ -25,17 +30,22 @@ self._file = f def __call__(self, msg): + """ write a message to the log """ if not hasattr(self, "_file"): self._openfile() print >> self._file, msg def STDOUT(msg): + """ consumer that writes to sys.stdout """ print >>sys.stdout, str(msg) def STDERR(msg): + """ consumer that writes to sys.stderr """ print >>sys.stderr, str(msg) class Syslog: + """ consumer that writes to the syslog daemon """ + for priority in "LOG_EMERG LOG_ALERT LOG_CRIT LOG_ERR LOG_WARNING LOG_NOTICE LOG_INFO LOG_DEBUG".split(): try: exec("%s = py.std.syslog.%s" % (priority, priority)) @@ -48,10 +58,12 @@ self.priority = priority def __call__(self, msg): + """ write a message to the log """ py.std.syslog.syslog(self.priority, str(msg)) def setconsumer(keywords, consumer): + """ create a consumer for a set of keywords """ # normalize to tuples if isinstance(keywords, str): keywords = tuple(map(None, keywords.split())) Modified: py/trunk/py/log/logger.py ============================================================================== --- py/trunk/py/log/logger.py (original) +++ py/trunk/py/log/logger.py Sun Feb 4 22:47:03 2007 @@ -61,9 +61,11 @@ setattr(self, name, Processor(self, name, dest)) def get(ident="global", **kwargs): + """ return the Logger with id 'ident', instantiating if appropriate """ try: log = Logger._key2logger[ident] except KeyError: log = Logger(ident) log.ensure_sub(**kwargs) return log + Modified: py/trunk/py/log/producer.py ============================================================================== --- py/trunk/py/log/producer.py (original) +++ py/trunk/py/log/producer.py Sun Feb 4 22:47:03 2007 @@ -49,11 +49,18 @@ return producer def __call__(self, *args): + """ write a message to the appropriate consumer(s) """ func = self.get_consumer(self.keywords) if func is not None: func(self.Message(self.keywords, args)) def get_consumer(self, keywords): + """ return a consumer matching keywords + + tries to find the most suitable consumer by walking, starting from + the back, the list of keywords, the first consumer matching a + keyword is returned (falling back to py.log.default) + """ for i in range(len(self.keywords), 0, -1): try: return self.keywords2consumer[self.keywords[:i]] @@ -62,6 +69,7 @@ return self.keywords2consumer.get('default', default_consumer) def set_consumer(self, consumer): + """ register a consumer matching our own keywords """ self.keywords2consumer[self.keywords] = consumer default = Producer('default') @@ -74,6 +82,7 @@ Producer.keywords2consumer.update(state) def default_consumer(msg): + """ the default consumer, prints the message to stdout (using 'print') """ print str(msg) Producer.keywords2consumer['default'] = default_consumer From guido at codespeak.net Sun Feb 4 22:49:45 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Sun, 4 Feb 2007 22:49:45 +0100 (CET) Subject: [py-svn] r37931 - py/trunk/py/apigen Message-ID: <20070204214945.BC37E10069@code0.codespeak.net> Author: guido Date: Sun Feb 4 22:49:44 2007 New Revision: 37931 Modified: py/trunk/py/apigen/htmlgen.py Log: Made that the __init__ method is the first method displayed. Modified: py/trunk/py/apigen/htmlgen.py ============================================================================== --- py/trunk/py/apigen/htmlgen.py (original) +++ py/trunk/py/apigen/htmlgen.py Sun Feb 4 22:49:44 2007 @@ -417,6 +417,9 @@ methods = self.dsa.get_class_methods(dotted_name) if methods: ret.append(H.h2('methods:')) + if '__init__' in methods: + methods.remove('__init__') + methods.insert(0, '__init__') for method in methods: ret += self.build_callable_view('%s.%s' % (dotted_name, method)) From hpk at codespeak.net Mon Feb 5 00:12:15 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 5 Feb 2007 00:12:15 +0100 (CET) Subject: [py-svn] r37935 - in py/trunk/py/test/rsession: . testing Message-ID: <20070204231215.7F5ED1006E@code0.codespeak.net> Author: hpk Date: Mon Feb 5 00:12:12 2007 New Revision: 37935 Modified: py/trunk/py/test/rsession/hostmanage.py py/trunk/py/test/rsession/master.py py/trunk/py/test/rsession/rsession.py py/trunk/py/test/rsession/testing/test_master.py Log: remove done_dict and according experimental code for re-scheduling (i guess) fijal: in the diff you'll find a XXX fijal, i changed the meaning of a test, wasn't sure about it. can you check? Modified: py/trunk/py/test/rsession/hostmanage.py ============================================================================== --- py/trunk/py/test/rsession/hostmanage.py (original) +++ py/trunk/py/test/rsession/hostmanage.py Mon Feb 5 00:12:12 2007 @@ -124,19 +124,17 @@ finishedcallback=donecallback) rsync.send(root) - def init_hosts(self, reporter, done_dict=None): - if done_dict is None: - done_dict = {} + def init_hosts(self, reporter): # hosts ready self.init_rsync(reporter) - return self.setup_nodes(reporter, done_dict) + return self.setup_nodes(reporter) - def setup_nodes(self, reporter, done_dict): + def setup_nodes(self, reporter): nodes = [] for host in self.sshhosts: if hasattr(host.gw, 'remote_exec'): # otherwise dummy for tests :/ ch = setup_slave(host, self.config) - nodes.append(MasterNode(ch, reporter, done_dict)) + nodes.append(MasterNode(ch, reporter)) return nodes def teardown_hosts(self, reporter, channels, nodes, Modified: py/trunk/py/test/rsession/master.py ============================================================================== --- py/trunk/py/test/rsession/master.py (original) +++ py/trunk/py/test/rsession/master.py Mon Feb 5 00:12:12 2007 @@ -6,17 +6,15 @@ from py.__.test.rsession import report class MasterNode(object): - def __init__(self, channel, reporter, done_dict): + def __init__(self, channel, reporter): self.channel = channel self.reporter = reporter - - def callback(outcome): - item = self.pending.pop() - if not item in done_dict: - self.receive_result(outcome, item) - done_dict[item] = True - channel.setcallback(callback) self.pending = [] + channel.setcallback(self._callback) + + def _callback(self, outcome): + item = self.pending.pop() + self.receive_result(outcome, item) def receive_result(self, outcomestring, item): repr_outcome = ReprOutcome(outcomestring) @@ -39,19 +37,6 @@ for y in x._tryiter(reporterror = lambda x: reporterror(reporter, x), keyword = keyword): yield y -def randomgen(items, done_dict): - """ Generator, which randomly gets all the tests from items as long - as they're not in done_dict - """ - import random - while items: - values = items.keys() - item = values[int(random.random()*len(values))] - if item in done_dict: - del items[item] - else: - yield item - def dispatch_loop(masternodes, itemgenerator, shouldstop, waiter = lambda: py.std.time.sleep(0.1), max_tasks_per_node=None): Modified: py/trunk/py/test/rsession/rsession.py ============================================================================== --- py/trunk/py/test/rsession/rsession.py (original) +++ py/trunk/py/test/rsession/rsession.py Mon Feb 5 00:12:12 2007 @@ -9,8 +9,7 @@ import time from py.__.test.rsession import report -from py.__.test.rsession.master import \ - MasterNode, dispatch_loop, itemgen, randomgen +from py.__.test.rsession.master import MasterNode, dispatch_loop, itemgen from py.__.test.rsession.hostmanage import HostInfo, HostManager from py.__.test.rsession.local import local_loop, plain_runner, apigen_runner,\ box_runner @@ -133,13 +132,12 @@ reporter(report.TestStarted(sshhosts)) - done_dict = {} hostmanager = HostManager(sshhosts, self.config) try: - nodes = hostmanager.init_hosts(reporter, done_dict) + nodes = hostmanager.init_hosts(reporter) reporter(report.RsyncFinished()) try: - self.dispatch_tests(nodes, reporter, checkfun, done_dict) + self.dispatch_tests(nodes, reporter, checkfun) except (KeyboardInterrupt, SystemExit): print >>sys.stderr, "C-c pressed waiting for gateways to teardown..." channels = [node.channel for node in nodes] @@ -168,20 +166,12 @@ return [HostInfo(spec) for spec in self.config.getvalue("dist_hosts")] - def dispatch_tests(self, nodes, reporter, checkfun, done_dict): + def dispatch_tests(self, nodes, reporter, checkfun): colitems = self.config.getcolitems() keyword = self.config.option.keyword itemgenerator = itemgen(colitems, reporter, keyword, self.reporterror) all_tests = dispatch_loop(nodes, itemgenerator, checkfun) - #if all_tests: - # todo = {} - # for key, value in all_tests.items(): - # if key not in done_dict: - # todo[key] = True - # rg = randomgen(todo, done_dict) - # dispatch_loop(nodes, rg, lambda:False, max_tasks_per_node=1) - class LSession(AbstractSession): """ Local version of session Modified: py/trunk/py/test/rsession/testing/test_master.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_master.py (original) +++ py/trunk/py/test/rsession/testing/test_master.py Mon Feb 5 00:12:12 2007 @@ -9,7 +9,7 @@ if sys.platform == 'win32': py.test.skip("rsession is unsupported on Windows.") -from py.__.test.rsession.master import dispatch_loop, MasterNode, randomgen +from py.__.test.rsession.master import dispatch_loop, MasterNode from py.__.test.rsession.slave import setup_slave from py.__.test.rsession.outcome import ReprOutcome, Outcome from py.__.test.rsession import report @@ -51,7 +51,7 @@ ch = DummyChannel() reportlist = [] - mnode = MasterNode(ch, reportlist.append, {}) + mnode = MasterNode(ch, reportlist.append) mnode.send(Item("ok")) mnode.send(Item("notok")) ch.callback(Outcome().make_repr()) @@ -62,15 +62,19 @@ assert received[0].outcome.passed assert not received[1].outcome.passed -def test_unique_nodes(): +def test_sending_two_noes(): + # XXX fijal: this test previously tested that the second + # item result would not get send. why? did i miss + # something? + # ch = DummyChannel() reportlist = [] - mnode = MasterNode(ch, reportlist.append, {}) + mnode = MasterNode(ch, reportlist.append) mnode.send(Item("ok")) mnode.send(Item("ok")) ch.callback(Outcome().make_repr()) ch.callback(Outcome().make_repr()) - assert len(reportlist) == 3 + assert len(reportlist) == 4 def test_outcome_repr(): out = ReprOutcome(Outcome(skipped=True).make_repr()) @@ -184,15 +188,3 @@ # XXX: We have to wait here a bit to make sure that it really did happen channel.waitclose(2) -def test_randomgen(): - d = {} - gen = randomgen({1:True, 2:True, 3:True}, d) - for i in range(100): - assert gen.next() in [1,2,3] - d[3] = True - for i in range(100): - assert gen.next() in [1,2] - d[2] = True - d[1] = True - py.test.raises(StopIteration, "gen.next()") - From hpk at codespeak.net Mon Feb 5 00:21:37 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 5 Feb 2007 00:21:37 +0100 (CET) Subject: [py-svn] r37936 - py/trunk/py/test/rsession Message-ID: <20070204232137.245E31006E@code0.codespeak.net> Author: hpk Date: Mon Feb 5 00:21:35 2007 New Revision: 37936 Modified: py/trunk/py/test/rsession/hostmanage.py py/trunk/py/test/rsession/master.py py/trunk/py/test/rsession/rsession.py Log: minor cleanups Modified: py/trunk/py/test/rsession/hostmanage.py ============================================================================== --- py/trunk/py/test/rsession/hostmanage.py (original) +++ py/trunk/py/test/rsession/hostmanage.py Mon Feb 5 00:21:35 2007 @@ -105,10 +105,10 @@ dist_remotepython = self.config.getvalue("dist_remotepython") for host in self.sshhosts: host.initgateway(python=dist_remotepython) - host.gw.host = host # XXX would like to avoid it + host.gw.host = host def init_rsync(self, reporter): - # send each rsync roots + # send each rsync root roots = self.config.getvalue_pathlist("dist_rsync_roots") if roots is None: roots = [self.config.topdir] Modified: py/trunk/py/test/rsession/master.py ============================================================================== --- py/trunk/py/test/rsession/master.py (original) +++ py/trunk/py/test/rsession/master.py Mon Feb 5 00:21:35 2007 @@ -33,8 +33,10 @@ self.reporter(report.SendItem(self.channel, item)) def itemgen(colitems, reporter, keyword, reporterror): + def rep(x): + reporterror(reporter, x) for x in colitems: - for y in x._tryiter(reporterror = lambda x: reporterror(reporter, x), keyword = keyword): + for y in x._tryiter(reporterror=rep, keyword=keyword): yield y def dispatch_loop(masternodes, itemgenerator, shouldstop, Modified: py/trunk/py/test/rsession/rsession.py ============================================================================== --- py/trunk/py/test/rsession/rsession.py (original) +++ py/trunk/py/test/rsession/rsession.py Mon Feb 5 00:21:35 2007 @@ -68,13 +68,13 @@ return reporter, startserverflag def reporterror(reporter, data): - excinfo, item = data - if excinfo is None: - reporter(report.ItemStart(item)) - elif excinfo.type is Skipped: - reporter(report.SkippedTryiter(excinfo, item)) - else: - reporter(report.FailedTryiter(excinfo, item)) + excinfo, item = data + if excinfo is None: + reporter(report.ItemStart(item)) + elif excinfo.type is Skipped: + reporter(report.SkippedTryiter(excinfo, item)) + else: + reporter(report.FailedTryiter(excinfo, item)) reporterror = staticmethod(reporterror) def kill_server(self, startserverflag): From hpk at codespeak.net Mon Feb 5 00:34:27 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 5 Feb 2007 00:34:27 +0100 (CET) Subject: [py-svn] r37938 - in py/trunk/py/test/rsession: . testing Message-ID: <20070204233427.867171006E@code0.codespeak.net> Author: hpk Date: Mon Feb 5 00:34:23 2007 New Revision: 37938 Added: py/trunk/py/test/rsession/repevent.py - copied unchanged from r37927, py/trunk/py/test/rsession/report.py Removed: py/trunk/py/test/rsession/report.py Modified: py/trunk/py/test/rsession/executor.py py/trunk/py/test/rsession/hostmanage.py py/trunk/py/test/rsession/local.py py/trunk/py/test/rsession/master.py py/trunk/py/test/rsession/reporter.py py/trunk/py/test/rsession/rest.py py/trunk/py/test/rsession/rsession.py py/trunk/py/test/rsession/testing/test_lsession.py py/trunk/py/test/rsession/testing/test_master.py py/trunk/py/test/rsession/testing/test_report.py py/trunk/py/test/rsession/testing/test_reporter.py py/trunk/py/test/rsession/testing/test_rest.py py/trunk/py/test/rsession/testing/test_rsession.py py/trunk/py/test/rsession/web.py Log: rename report.py to repevent.py (report events) also to ease the completion ambiguity a bit (and to avoid having to read "reporter" and "report" next to each other) Modified: py/trunk/py/test/rsession/executor.py ============================================================================== --- py/trunk/py/test/rsession/executor.py (original) +++ py/trunk/py/test/rsession/executor.py Mon Feb 5 00:34:23 2007 @@ -5,7 +5,7 @@ from py.__.test.rsession.outcome import Outcome, ReprOutcome from py.__.test.rsession.box import Box -from py.__.test.rsession import report +from py.__.test.rsession import repevent from py.__.test.outcome import Skipped, Failed class RunExecutor(object): @@ -41,7 +41,7 @@ outcome = Outcome(excinfo=excinfo, setupfailure=False) if self.usepdb: if self.reporter is not None: - self.reporter(report.ImmediateFailure(self.item, + self.reporter(repevent.ImmediateFailure(self.item, ReprOutcome(outcome.make_repr (self.config.option.tbstyle)))) import pdb Modified: py/trunk/py/test/rsession/hostmanage.py ============================================================================== --- py/trunk/py/test/rsession/hostmanage.py (original) +++ py/trunk/py/test/rsession/hostmanage.py Mon Feb 5 00:34:23 2007 @@ -5,7 +5,7 @@ from py.__.test.rsession.master import MasterNode from py.__.test.rsession.slave import setup_slave -from py.__.test.rsession import report +from py.__.test.rsession import repevent class HostInfo(object): """ Class trying to store all necessary attributes @@ -117,9 +117,9 @@ for root in roots: destrelpath = root.relto(self.config.topdir) for host in self.sshhosts: - reporter(report.HostRSyncing(host)) + reporter(repevent.HostRSyncing(host)) def donecallback(): - reporter(report.HostReady(host)) + reporter(repevent.HostReady(host)) rsync.add_target_host(host, destrelpath, finishedcallback=donecallback) rsync.send(root) @@ -158,7 +158,7 @@ def teardown_gateways(self, reporter, channels): for channel in channels: try: - report.wrapcall(reporter, channel.waitclose) + repevent.wrapcall(reporter, channel.waitclose) except KeyboardInterrupt, SystemExit: raise except: Modified: py/trunk/py/test/rsession/local.py ============================================================================== --- py/trunk/py/test/rsession/local.py (original) +++ py/trunk/py/test/rsession/local.py Mon Feb 5 00:34:23 2007 @@ -5,7 +5,7 @@ import py from py.__.test.rsession.executor import BoxExecutor, RunExecutor,\ ApigenExecutor -from py.__.test.rsession import report +from py.__.test.rsession import repevent from py.__.test.rsession.outcome import ReprOutcome # XXX copied from session.py @@ -68,6 +68,6 @@ if shouldstop(): return outcome = runner(item, session, reporter) - reporter(report.ReceivedItemOutcome(None, item, outcome)) + reporter(repevent.ReceivedItemOutcome(None, item, outcome)) except StopIteration: break Modified: py/trunk/py/test/rsession/master.py ============================================================================== --- py/trunk/py/test/rsession/master.py (original) +++ py/trunk/py/test/rsession/master.py Mon Feb 5 00:34:23 2007 @@ -3,7 +3,7 @@ """ import py from py.__.test.rsession.outcome import ReprOutcome -from py.__.test.rsession import report +from py.__.test.rsession import repevent class MasterNode(object): def __init__(self, channel, reporter): @@ -19,7 +19,7 @@ def receive_result(self, outcomestring, item): repr_outcome = ReprOutcome(outcomestring) # send finish report - self.reporter(report.ReceivedItemOutcome( + self.reporter(repevent.ReceivedItemOutcome( self.channel, item, repr_outcome)) def send(self, item): @@ -30,7 +30,7 @@ #itemspec = item.listnames()[1:] self.channel.send(item._get_collector_trail()) # send start report - self.reporter(report.SendItem(self.channel, item)) + self.reporter(repevent.SendItem(self.channel, item)) def itemgen(colitems, reporter, keyword, reporterror): def rep(x): Deleted: /py/trunk/py/test/rsession/report.py ============================================================================== --- /py/trunk/py/test/rsession/report.py Mon Feb 5 00:34:23 2007 +++ (empty file) @@ -1,132 +0,0 @@ -""" Reporter classes for showing asynchronous and synchronous status events -""" - -import py -import time - -def basic_report(msg_type, message): - print msg_type, message - -#def report(msg_type, message): -# pass - -##def report_error(excinfo): -## if isinstance(excinfo, py.test.Item.Skipped): -## # we need to dispatch this info -## report(Skipped(excinfo)) -## else: -## report("itererror", excinfo) - -def wrapcall(reporter, func, *args, **kwargs): - reporter(CallStart(func, args, kwargs)) - try: - retval = func(*args, **kwargs) - except: - reporter(CallException(func, args, kwargs)) - raise - else: - reporter(CallFinish(func, args, kwargs)) - return retval - -# ---------------------------------------------------------------------- -# Reporting Events -# ---------------------------------------------------------------------- - -class ReportEvent(object): - def __repr__(self): - l = ["%s=%s" %(key, value) - for key, value in self.__dict__.items()] - return "<%s %s>" %(self.__class__.__name__, " ".join(l),) - -class SendItem(ReportEvent): - def __init__(self, channel, item): - self.item = item - self.channel = channel - if channel: - self.host = channel.gateway.host - -class ReceivedItemOutcome(ReportEvent): - def __init__(self, channel, item, outcome): - self.channel = channel - if channel: - self.host = channel.gateway.host - self.item = item - self.outcome = outcome - -class CallEvent(ReportEvent): - def __init__(self, func, args, kwargs): - self.func = func - self.args = args - self.kwargs = kwargs - - def __repr__(self): - call = "%s args=%s, kwargs=%s" %(self.func.__name__, - self.args, self.kwargs) - return '<%s %s>' %(self.__class__.__name__, call) - -class CallStart(CallEvent): - pass - -class CallException(CallEvent): - pass - -class CallFinish(CallEvent): - pass - -class HostRSyncing(ReportEvent): - def __init__(self, host): - self.host = host - -class HostReady(ReportEvent): - def __init__(self, host): - self.host = host - -class TestStarted(ReportEvent): - def __init__(self, hosts): - self.hosts = hosts - self.timestart = time.time() - -class TestFinished(ReportEvent): - def __init__(self): - self.timeend = time.time() - -class Nodes(ReportEvent): - def __init__(self, nodes): - self.nodes = nodes - -class SkippedTryiter(ReportEvent): - def __init__(self, excinfo, item): - self.excinfo = excinfo - self.item = item - -class FailedTryiter(ReportEvent): - def __init__(self, excinfo, item): - self.excinfo = excinfo - self.item = item - -class ItemStart(ReportEvent): - """ This class shows most of the start stuff, like directory, module, class - can be used for containers - """ - def __init__(self, item): - self.item = item - -class RsyncFinished(ReportEvent): - def __init__(self): - self.time = time.time() - -class ImmediateFailure(ReportEvent): - def __init__(self, item, outcome): - self.item = item - self.outcome = outcome - -class PongReceived(ReportEvent): - def __init__(self, hostid, result): - self.hostid = hostid - self.result = result - -class InterruptedExecution(ReportEvent): - pass - -class CrashedExecution(ReportEvent): - pass Modified: py/trunk/py/test/rsession/reporter.py ============================================================================== --- py/trunk/py/test/rsession/reporter.py (original) +++ py/trunk/py/test/rsession/reporter.py Mon Feb 5 00:34:23 2007 @@ -8,7 +8,7 @@ import py from py.__.test.terminal.out import getout -from py.__.test.rsession import report +from py.__.test.rsession import repevent from py.__.test.rsession import outcome from py.__.misc.terminal_helper import ansi_print, get_terminal_width from py.__.test.representation import Presenter @@ -108,7 +108,7 @@ if self.failed_tests_outcome: self.out.sep("=", " FAILURES ") for event in self.failed_tests_outcome: - if isinstance(event, report.ReceivedItemOutcome): + if isinstance(event, repevent.ReceivedItemOutcome): host = self.gethost(event) self.out.sep('_', "%s on %s" % (" ".join(event.item.listnames()), host)) @@ -152,11 +152,11 @@ texts = {} for event in self.skipped_tests_outcome: colitem = event.item - if isinstance(event, report.ReceivedItemOutcome): + if isinstance(event, repevent.ReceivedItemOutcome): outcome = event.outcome text = outcome.skipped itemname = self.get_item_name(event, colitem) - elif isinstance(event, report.SkippedTryiter): + elif isinstance(event, repevent.SkippedTryiter): text = str(event.excinfo.value) itemname = "/".join(colitem.listnames()) if text not in texts: Modified: py/trunk/py/test/rsession/rest.py ============================================================================== --- py/trunk/py/test/rsession/rest.py (original) +++ py/trunk/py/test/rsession/rest.py Mon Feb 5 00:34:23 2007 @@ -6,7 +6,7 @@ import sys from StringIO import StringIO from py.__.test.rsession.reporter import AbstractReporter -from py.__.test.rsession import report +from py.__.test.rsession import repevent from py.__.rest.rst import * class RestReporter(AbstractReporter): @@ -122,11 +122,11 @@ texts = {} for event in self.skipped_tests_outcome: colitem = event.item - if isinstance(event, report.ReceivedItemOutcome): + if isinstance(event, repevent.ReceivedItemOutcome): outcome = event.outcome text = outcome.skipped itemname = self.get_item_name(event, colitem) - elif isinstance(event, report.SkippedTryiter): + elif isinstance(event, repevent.SkippedTryiter): text = str(event.excinfo.value) itemname = "/".join(colitem.listnames()) if text not in texts: @@ -153,7 +153,7 @@ for i, event in enumerate(self.failed_tests_outcome): if i > 0: self.add_rest(Transition()) - if isinstance(event, report.ReceivedItemOutcome): + if isinstance(event, repevent.ReceivedItemOutcome): host = self.get_host(event) itempath = self.get_path_from_item(event.item) root = self.get_rootpath(event.item) Modified: py/trunk/py/test/rsession/rsession.py ============================================================================== --- py/trunk/py/test/rsession/rsession.py (original) +++ py/trunk/py/test/rsession/rsession.py Mon Feb 5 00:34:23 2007 @@ -8,7 +8,7 @@ import re import time -from py.__.test.rsession import report +from py.__.test.rsession import repevent from py.__.test.rsession.master import MasterNode, dispatch_loop, itemgen from py.__.test.rsession.hostmanage import HostInfo, HostManager from py.__.test.rsession.local import local_loop, plain_runner, apigen_runner,\ @@ -70,11 +70,11 @@ def reporterror(reporter, data): excinfo, item = data if excinfo is None: - reporter(report.ItemStart(item)) + reporter(repevent.ItemStart(item)) elif excinfo.type is Skipped: - reporter(report.SkippedTryiter(excinfo, item)) + reporter(repevent.SkippedTryiter(excinfo, item)) else: - reporter(report.FailedTryiter(excinfo, item)) + reporter(repevent.FailedTryiter(excinfo, item)) reporterror = staticmethod(reporterror) def kill_server(self, startserverflag): @@ -90,7 +90,7 @@ """ self.was_failure = False def new_reporter(event): - if isinstance(event, report.ReceivedItemOutcome) and \ + if isinstance(event, repevent.ReceivedItemOutcome) and \ not event.outcome.passed and \ not event.outcome.skipped: self.was_failure = True @@ -130,12 +130,12 @@ sshhosts, RemoteReporter) reporter, checkfun = self.wrap_reporter(reporter) - reporter(report.TestStarted(sshhosts)) + reporter(repevent.TestStarted(sshhosts)) hostmanager = HostManager(sshhosts, self.config) try: nodes = hostmanager.init_hosts(reporter) - reporter(report.RsyncFinished()) + reporter(repevent.RsyncFinished()) try: self.dispatch_tests(nodes, reporter, checkfun) except (KeyboardInterrupt, SystemExit): @@ -149,16 +149,16 @@ channels = [node.channel for node in nodes] hostmanager.teardown_hosts(reporter, channels, nodes, exitfirst=self.config.option.exitfirst) - reporter(report.Nodes(nodes)) - retval = reporter(report.TestFinished()) + reporter(repevent.Nodes(nodes)) + retval = reporter(repevent.TestFinished()) self.kill_server(startserverflag) return retval except (KeyboardInterrupt, SystemExit): - reporter(report.InterruptedExecution()) + reporter(repevent.InterruptedExecution()) self.kill_server(startserverflag) raise except: - reporter(report.CrashedExecution()) + reporter(repevent.CrashedExecution()) self.kill_server(startserverflag) raise @@ -189,9 +189,9 @@ sshhosts, LocalReporter, args[0]) reporter, checkfun = self.wrap_reporter(reporter) - reporter(report.TestStarted(sshhosts)) + reporter(repevent.TestStarted(sshhosts)) colitems = self.config.getcolitems() - reporter(report.RsyncFinished()) + reporter(repevent.RsyncFinished()) if runner is None: runner = self.init_runner() @@ -201,7 +201,7 @@ itemgenerator = itemgen(colitems, reporter, keyword, self.reporterror) local_loop(self, reporter, itemgenerator, checkfun, self.config, runner=runner) - retval = reporter(report.TestFinished()) + retval = reporter(repevent.TestFinished()) self.kill_server(startserverflag) if not self.config.option.nomagic: Modified: py/trunk/py/test/rsession/testing/test_lsession.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_lsession.py (original) +++ py/trunk/py/test/rsession/testing/test_lsession.py Mon Feb 5 00:34:23 2007 @@ -4,7 +4,7 @@ import py from py.__.test.rsession.rsession import LSession -from py.__.test.rsession import report +from py.__.test.rsession import repevent from py.__.test.rsession.local import box_runner, plain_runner, apigen_runner def setup_module(mod): @@ -37,7 +37,7 @@ allevents = [] lsession.main(reporter=allevents.append, runner=runner) testevents = [x for x in allevents - if isinstance(x, report.ReceivedItemOutcome)] + if isinstance(x, repevent.ReceivedItemOutcome)] assert len(testevents) passevents = [i for i in testevents if i.outcome.passed] failevents = [i for i in testevents if i.outcome.excinfo] @@ -97,7 +97,7 @@ else: py.test.fail("Didn't raise system exit") failure_events = [event for event in allevents if isinstance(event, - report.ImmediateFailure)] + repevent.ImmediateFailure)] assert len(failure_events) == 1 assert len(l) == 1 finally: @@ -127,7 +127,7 @@ lsession.main(reporter=allevents.append, runner=box_runner) testevents = [x for x in allevents - if isinstance(x, report.ReceivedItemOutcome)] + if isinstance(x, repevent.ReceivedItemOutcome)] assert len(testevents) passevents = [i for i in testevents if i.outcome.passed] failevents = [i for i in testevents if i.outcome.excinfo] @@ -156,7 +156,7 @@ lsession.main(reporter=allevents.append, runner=box_runner) testevents = [x for x in allevents - if isinstance(x, report.ReceivedItemOutcome)] + if isinstance(x, repevent.ReceivedItemOutcome)] assert len(testevents) passevents = [i for i in testevents if i.outcome.passed] failevents = [i for i in testevents if i.outcome.excinfo] @@ -190,7 +190,7 @@ assert len(allruns) == 4 testevents = [x for x in allevents - if isinstance(x, report.ReceivedItemOutcome)] + if isinstance(x, repevent.ReceivedItemOutcome)] assert len(testevents) == 4 lst = ['test_one', 'test_one_one', 'test_other', 'test_two'] for num, i in enumerate(testevents): @@ -214,13 +214,13 @@ allevents = [] lsession.main(reporter=allevents.append, runner=box_runner) testevents = [x for x in allevents - if isinstance(x, report.ReceivedItemOutcome)] + if isinstance(x, repevent.ReceivedItemOutcome)] assert len(testevents) == 0 failedtryiter = [x for x in allevents - if isinstance(x, report.FailedTryiter)] + if isinstance(x, repevent.FailedTryiter)] assert len(failedtryiter) == 1 skippedtryiter = [x for x in allevents - if isinstance(x, report.SkippedTryiter)] + if isinstance(x, repevent.SkippedTryiter)] assert len(skippedtryiter) == 1 @@ -240,7 +240,7 @@ allevents = [] lsession.main(reporter=allevents.append, runner=box_runner) testevents = [x for x in allevents - if isinstance(x, report.ReceivedItemOutcome)] + if isinstance(x, repevent.ReceivedItemOutcome)] failevents = [i for i in testevents if i.outcome.excinfo] assert len(failevents) == 1 assert len(testevents) == 1 @@ -261,7 +261,7 @@ allevents = [] lsession.main(reporter=allevents.append, runner=plain_runner) testevents = [x for x in allevents - if isinstance(x, report.ReceivedItemOutcome)] + if isinstance(x, repevent.ReceivedItemOutcome)] assert len(testevents) == 1 assert testevents[0].outcome.passed assert testevents[0].outcome.stderr == "" Modified: py/trunk/py/test/rsession/testing/test_master.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_master.py (original) +++ py/trunk/py/test/rsession/testing/test_master.py Mon Feb 5 00:34:23 2007 @@ -12,7 +12,7 @@ from py.__.test.rsession.master import dispatch_loop, MasterNode from py.__.test.rsession.slave import setup_slave from py.__.test.rsession.outcome import ReprOutcome, Outcome -from py.__.test.rsession import report +from py.__.test.rsession import repevent from py.__.test.rsession.hostmanage import HostInfo def setup_module(mod): @@ -58,7 +58,7 @@ ch.callback(Outcome(excinfo=excinfo).make_repr()) assert len(reportlist) == 4 received = [i for i in reportlist - if isinstance(i, report.ReceivedItemOutcome)] + if isinstance(i, repevent.ReceivedItemOutcome)] assert received[0].outcome.passed assert not received[1].outcome.passed @@ -134,7 +134,7 @@ def test_slave_running(self): py.test.skip("XXX test broken, needs refactoring") def simple_report(event): - if not isinstance(event, report.ReceivedItemOutcome): + if not isinstance(event, repevent.ReceivedItemOutcome): return item = event.item if item.code.name == 'funcpass': @@ -162,7 +162,7 @@ def test_slave_running_interrupted(): py.test.skip("XXX test broken, needs refactoring") #def simple_report(event): - # if not isinstance(event, report.ReceivedItemOutcome): + # if not isinstance(event, repevent.ReceivedItemOutcome): # return # item = event.item # if item.code.name == 'funcpass': Modified: py/trunk/py/test/rsession/testing/test_report.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_report.py (original) +++ py/trunk/py/test/rsession/testing/test_report.py Mon Feb 5 00:34:23 2007 @@ -1,17 +1,17 @@ """ test reporting functionality. """ import py -from py.__.test.rsession import report +from py.__.test.rsession import repevent def test_wrapcall_ok(): l = [] def ok(x): return x+1 - i = report.wrapcall(l.append, ok, 1) + i = repevent.wrapcall(l.append, ok, 1) assert i == 2 assert len(l) == 2 - assert isinstance(l[0], report.CallStart) - assert isinstance(l[1], report.CallFinish) + assert isinstance(l[0], repevent.CallStart) + assert isinstance(l[1], repevent.CallFinish) assert repr(l[0]) assert repr(l[1]) @@ -19,18 +19,18 @@ l = [] def fail(x): raise ValueError - py.test.raises(ValueError, "report.wrapcall(l.append, fail, 1)") + py.test.raises(ValueError, "repevent.wrapcall(l.append, fail, 1)") assert len(l) == 2 - assert isinstance(l[0], report.CallStart) - assert isinstance(l[1], report.CallException) + assert isinstance(l[0], repevent.CallStart) + assert isinstance(l[1], repevent.CallException) def test_reporter_methods_sanity(): """ Checks if all the methods of reporter are sane """ from py.__.test.rsession.rsession import RemoteReporter - from py.__.test.rsession import report + from py.__.test.rsession import repevent for method in dir(RemoteReporter): if method.startswith("report_") and method != "report_unknown": - assert method[len('report_'):] in report.__dict__ + assert method[len('report_'):] in repevent.__dict__ Modified: py/trunk/py/test/rsession/testing/test_reporter.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_reporter.py (original) +++ py/trunk/py/test/rsession/testing/test_reporter.py Mon Feb 5 00:34:23 2007 @@ -21,7 +21,7 @@ #py.test.skip("in progress") from py.__.test.rsession.rsession import LocalReporter, AbstractSession,\ RemoteReporter -from py.__.test.rsession import report +from py.__.test.rsession import repevent from py.__.test.rsession.outcome import ReprOutcome, Outcome from py.__.test.rsession.testing.test_slave import funcpass_spec, mod_spec from py.__.test.rsession.hostmanage import HostInfo @@ -72,7 +72,7 @@ r = self.reporter(config, hosts) ch = DummyChannel(hosts[0]) for outcome in outcomes: - r.report(report.ReceivedItemOutcome(ch, item, outcome)) + r.report(repevent.ReceivedItemOutcome(ch, item, outcome)) cap = py.io.StdCaptureFD() boxfun(config, item, outcomes) @@ -92,10 +92,10 @@ hosts = [HostInfo('localhost')] r = self.reporter(config, hosts) #r.pkgdir = pkdgir - r.report(report.ItemStart(item)) + r.report(repevent.ItemStart(item)) ch = DummyChannel(hosts[0]) for outcome in outcomes: - r.report(report.ReceivedItemOutcome(ch, funcitem, outcome)) + r.report(repevent.ReceivedItemOutcome(ch, funcitem, outcome)) cap = py.io.StdCaptureFD() boxfun(self.pkgdir, config, moditem, funcitem, outcomes) @@ -142,10 +142,10 @@ rootcol = py.test.collect.Directory(tmpdir) host = HostInfo('localhost') r = self.reporter(config, [host]) - r.report(report.TestStarted([host])) - r.report(report.RsyncFinished()) + r.report(repevent.TestStarted([host])) + r.report(repevent.RsyncFinished()) list(rootcol._tryiter(reporterror=lambda x : AbstractSession.reporterror(r.report, x))) - r.report(report.TestFinished()) + r.report(repevent.TestFinished()) cap = py.io.StdCaptureFD() boxfun() @@ -160,10 +160,10 @@ config = py.test.config._reparse([str(tmpdir)]) hosts = [HostInfo(i) for i in ["host1", "host2", "host3"]] r = self.reporter(config, hosts) - r.report(report.TestStarted(hosts)) - r.report(report.HostReady(hosts[0])) - r.report(report.HostReady(hosts[1])) - r.report(report.HostReady(hosts[2])) + r.report(repevent.TestStarted(hosts)) + r.report(repevent.HostReady(hosts[0])) + r.report(repevent.HostReady(hosts[1])) + r.report(repevent.HostReady(hosts[2])) out, err = cap.reset() assert not err expected1 = "Test started, hosts: host1, host2, host3" Modified: py/trunk/py/test/rsession/testing/test_rest.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_rest.py (original) +++ py/trunk/py/test/rsession/testing/test_rest.py Mon Feb 5 00:34:23 2007 @@ -5,7 +5,7 @@ import py from py.__.test.rsession.testing.test_reporter import AbstractTestReporter,\ DummyChannel -from py.__.test.rsession import report +from py.__.test.rsession import repevent from py.__.test.rsession.rest import RestReporter, NoLinkWriter from py.__.rest.rst import * from py.__.test.rsession.hostmanage import HostInfo @@ -41,7 +41,7 @@ self.config.option.verbose = False def test_report_SendItem(self): - event = report.SendItem(item='foo/bar.py', channel=ch) + event = repevent.SendItem(item='foo/bar.py', channel=ch) reporter.report(event) assert stdout.getvalue() == '' stdout.seek(0) @@ -52,18 +52,18 @@ 'localhost\n\n') def test_report_HostRSyncing(self): - event = report.HostRSyncing(HostInfo('localhost:/foo/bar')) + event = repevent.HostRSyncing(HostInfo('localhost:/foo/bar')) reporter.report(event) assert stdout.getvalue() == ('::\n\n localhost: RSYNC ==> ' '/foo/bar\n\n') def test_report_HostReady(self): - event = report.HostReady(HostInfo('localhost')) + event = repevent.HostReady(HostInfo('localhost')) reporter.report(event) assert stdout.getvalue() == '::\n\n localhost: READY\n\n' def test_report_TestStarted(self): - event = report.TestStarted(hosts=[HostInfo('localhost'), + event = repevent.TestStarted(hosts=[HostInfo('localhost'), HostInfo('foo.com')]) reporter.report(event) assert stdout.getvalue() == """\ @@ -84,7 +84,7 @@ return ['package', 'foo', 'bar.py'] parent = Container(parent=None, fspath=py.path.local('.')) - event = report.ItemStart(item=FakeModule(parent)) + event = repevent.ItemStart(item=FakeModule(parent)) reporter.report(event) assert stdout.getvalue() == """\ Testing module foo/bar.py (2 items) @@ -106,7 +106,7 @@ def test_ReceivedItemOutcome_PASSED(self): outcome = Outcome() item = Container(listnames=lambda: ['', 'foo.py', 'bar', '()', 'baz']) - event = report.ReceivedItemOutcome(channel=ch, outcome=outcome, item=item) + event = repevent.ReceivedItemOutcome(channel=ch, outcome=outcome, item=item) reporter.report(event) assert stdout.getvalue() == ('* localhost\\: **PASSED** ' 'foo.py/bar()/baz\n\n') @@ -114,7 +114,7 @@ def test_ReceivedItemOutcome_SKIPPED(self): outcome = Outcome(skipped="reason") item = Container(listnames=lambda: ['', 'foo.py', 'bar', '()', 'baz']) - event = report.ReceivedItemOutcome(channel=ch, outcome=outcome, item=item) + event = repevent.ReceivedItemOutcome(channel=ch, outcome=outcome, item=item) reporter.report(event) assert stdout.getvalue() == ('* localhost\\: **SKIPPED** ' 'foo.py/bar()/baz\n\n') @@ -122,7 +122,7 @@ def test_ReceivedItemOutcome_FAILED(self): outcome = Outcome(excinfo="xxx") item = Container(listnames=lambda: ['', 'foo.py', 'bar', '()', 'baz']) - event = report.ReceivedItemOutcome(channel=ch, outcome=outcome, item=item) + event = repevent.ReceivedItemOutcome(channel=ch, outcome=outcome, item=item) reporter.report(event) assert stdout.getvalue() == """\ * localhost\: **FAILED** `traceback0`_ foo.py/bar()/baz @@ -154,7 +154,7 @@ parent = Container(parent=None, fspath=py.path.local('.')) item = Container(listnames=lambda: ['', 'foo.py', 'bar', '()', 'baz'], parent=parent, fspath=py.path.local('foo')) - event = report.ReceivedItemOutcome(channel=ch, outcome=outcome, + event = repevent.ReceivedItemOutcome(channel=ch, outcome=outcome, item=item) reporter.report(event) reporter.timestart = 10 @@ -167,10 +167,10 @@ assert out.find('') > -1 def test_skips(self): - class FakeOutcome(Container, report.ReceivedItemOutcome): + class FakeOutcome(Container, repevent.ReceivedItemOutcome): pass - class FakeTryiter(Container, report.SkippedTryiter): + class FakeTryiter(Container, repevent.SkippedTryiter): pass reporter.skips() @@ -192,7 +192,7 @@ """ def test_failures(self): - class FakeOutcome(Container, report.ReceivedItemOutcome): + class FakeOutcome(Container, repevent.ReceivedItemOutcome): pass parent = Container(parent=None, fspath=py.path.local('.')) Modified: py/trunk/py/test/rsession/testing/test_rsession.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_rsession.py (original) +++ py/trunk/py/test/rsession/testing/test_rsession.py Mon Feb 5 00:34:23 2007 @@ -3,7 +3,7 @@ """ import py -from py.__.test.rsession import report +from py.__.test.rsession import repevent from py.__.test.rsession.rsession import RSession from py.__.test.rsession.hostmanage import HostManager, HostInfo from py.__.test.rsession.testing.test_slave import funcfail_spec,\ @@ -56,7 +56,7 @@ allevents = [] rsession.main(reporter=allevents.append) testevents = [x for x in allevents - if isinstance(x, report.ReceivedItemOutcome)] + if isinstance(x, repevent.ReceivedItemOutcome)] assert len(testevents) == 3 assert rsession.checkfun() @@ -92,7 +92,7 @@ allevents = [] rsession.main(reporter=allevents.append) testevents = [x for x in allevents - if isinstance(x, report.ReceivedItemOutcome)] + if isinstance(x, repevent.ReceivedItemOutcome)] assert len(testevents) print testevents passevents = [i for i in testevents if i.outcome.passed] @@ -127,17 +127,17 @@ [node.channel for node in nodes], nodes) count_rsyn_calls = [i for i in setup_events - if isinstance(i, report.HostRSyncing)] + if isinstance(i, repevent.HostRSyncing)] assert len(count_rsyn_calls) == len([i for i in hosts]) count_ready_calls = [i for i in setup_events - if isinstance(i, report.HostReady)] + if isinstance(i, repevent.HostReady)] assert len(count_ready_calls) == len([i for i in hosts]) # same for teardown events teardown_wait_starts = [i for i in teardown_events - if isinstance(i, report.CallStart)] + if isinstance(i, repevent.CallStart)] teardown_wait_ends = [i for i in teardown_events - if isinstance(i, report.CallFinish)] + if isinstance(i, repevent.CallFinish)] assert len(teardown_wait_starts) == len(hosts) assert len(teardown_wait_ends) == len(hosts) @@ -168,7 +168,7 @@ hm.teardown_hosts(allevents.append, [node.channel for node in nodes], nodes) events = [i for i in allevents - if isinstance(i, report.ReceivedItemOutcome)] + if isinstance(i, repevent.ReceivedItemOutcome)] passed = [i for i in events if i.outcome.passed] skipped = [i for i in events @@ -201,7 +201,7 @@ allevents = [] rsession.main(reporter=allevents.append) testevents = [x for x in allevents - if isinstance(x, report.ReceivedItemOutcome)] + if isinstance(x, repevent.ReceivedItemOutcome)] passevents = [x for x in testevents if x.outcome.passed] assert len(passevents) == 1 Modified: py/trunk/py/test/rsession/web.py ============================================================================== --- py/trunk/py/test/rsession/web.py (original) +++ py/trunk/py/test/rsession/web.py Mon Feb 5 00:34:23 2007 @@ -15,7 +15,7 @@ import py from py.__.test.rsession.rsession import RSession -from py.__.test.rsession import report +from py.__.test.rsession import repevent from py.__.test import collect from py.__.test.rsession.webdata import json @@ -221,7 +221,7 @@ self.end_event.set() return {} # some dispatcher here - if isinstance(event, report.ReceivedItemOutcome): + if isinstance(event, repevent.ReceivedItemOutcome): args = {} outcome = event.outcome for key, val in outcome.__dict__.iteritems(): @@ -246,22 +246,22 @@ args['hostkey'] = event.channel.gateway.host.hostid else: args['hostkey'] = '' - elif isinstance(event, report.ItemStart): + elif isinstance(event, repevent.ItemStart): args = add_item(event) - elif isinstance(event, report.TestFinished): + elif isinstance(event, repevent.TestFinished): args = {} args['run'] = str(self.all) args['fails'] = str(len(self.fail_reasons)) args['skips'] = str(len(self.skip_reasons)) - elif isinstance(event, report.SendItem): + elif isinstance(event, repevent.SendItem): args = add_item(event) args['hostkey'] = event.channel.gateway.host.hostid - elif isinstance(event, report.HostReady): + elif isinstance(event, repevent.HostReady): self.ready_hosts[event.host] = True args = {'hostname' : event.host.hostname, 'hostkey' : event.host.hostid} - elif isinstance(event, report.FailedTryiter): + elif isinstance(event, repevent.FailedTryiter): args = add_item(event) - elif isinstance(event, report.SkippedTryiter): + elif isinstance(event, repevent.SkippedTryiter): args = add_item(event) args['reason'] = str(event.excinfo.value) else: From hpk at codespeak.net Mon Feb 5 01:14:13 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 5 Feb 2007 01:14:13 +0100 (CET) Subject: [py-svn] r37945 - in py/trunk/py/test/rsession: . testing Message-ID: <20070205001413.1673910074@code0.codespeak.net> Author: hpk Date: Mon Feb 5 01:14:11 2007 New Revision: 37945 Modified: py/trunk/py/test/rsession/hostmanage.py py/trunk/py/test/rsession/rsession.py py/trunk/py/test/rsession/testing/test_hostmanage.py py/trunk/py/test/rsession/testing/test_rsession.py Log: make the hostmanager get at the hosts itself (or you can pass it in a custom list of hosts) Modified: py/trunk/py/test/rsession/hostmanage.py ============================================================================== --- py/trunk/py/test/rsession/hostmanage.py (original) +++ py/trunk/py/test/rsession/hostmanage.py Mon Feb 5 01:14:11 2007 @@ -97,13 +97,16 @@ return True # added the target class HostManager(object): - def __init__(self, sshhosts, config): - self.sshhosts = sshhosts + def __init__(self, config, hosts=None): self.config = config + if hosts is None: + hosts = self.config.getvalue("dist_hosts") + hosts = [HostInfo(x) for x in hosts] + self.hosts = hosts def prepare_gateways(self): dist_remotepython = self.config.getvalue("dist_remotepython") - for host in self.sshhosts: + for host in self.hosts: host.initgateway(python=dist_remotepython) host.gw.host = host @@ -116,7 +119,7 @@ rsync = HostRSync() for root in roots: destrelpath = root.relto(self.config.topdir) - for host in self.sshhosts: + for host in self.hosts: reporter(repevent.HostRSyncing(host)) def donecallback(): reporter(repevent.HostReady(host)) @@ -131,7 +134,7 @@ def setup_nodes(self, reporter): nodes = [] - for host in self.sshhosts: + for host in self.hosts: if hasattr(host.gw, 'remote_exec'): # otherwise dummy for tests :/ ch = setup_slave(host, self.config) nodes.append(MasterNode(ch, reporter)) Modified: py/trunk/py/test/rsession/rsession.py ============================================================================== --- py/trunk/py/test/rsession/rsession.py (original) +++ py/trunk/py/test/rsession/rsession.py Mon Feb 5 01:14:11 2007 @@ -29,7 +29,7 @@ option.startserver = True super(AbstractSession, self).fixoptions() - def init_reporter(self, reporter, sshhosts, reporter_class, arg=""): + def init_reporter(self, reporter, hosts, reporter_class, arg=""): """ This initialises so called `reporter` class, which will handle all event presenting to user. Does not get called if main received custom reporter @@ -58,9 +58,9 @@ from py.__.test.rsession.rest import RestReporter reporter_class = RestReporter if arg: - reporter_instance = reporter_class(self.config, sshhosts) + reporter_instance = reporter_class(self.config, hosts) else: - reporter_instance = reporter_class(self.config, sshhosts) + reporter_instance = reporter_class(self.config, hosts) reporter = reporter_instance.report else: startserverflag = False @@ -125,16 +125,15 @@ """ main loop for running tests. """ args = self.config.args - sshhosts = self._getconfighosts() + hm = HostManager(self.config) reporter, startserverflag = self.init_reporter(reporter, - sshhosts, RemoteReporter) + hm.hosts, RemoteReporter) reporter, checkfun = self.wrap_reporter(reporter) - reporter(repevent.TestStarted(sshhosts)) + reporter(repevent.TestStarted(hm.hosts)) - hostmanager = HostManager(sshhosts, self.config) try: - nodes = hostmanager.init_hosts(reporter) + nodes = hm.init_hosts(reporter) reporter(repevent.RsyncFinished()) try: self.dispatch_tests(nodes, reporter, checkfun) @@ -162,10 +161,6 @@ self.kill_server(startserverflag) raise - def _getconfighosts(self): - return [HostInfo(spec) for spec in - self.config.getvalue("dist_hosts")] - def dispatch_tests(self, nodes, reporter, checkfun): colitems = self.config.getcolitems() keyword = self.config.option.keyword @@ -179,17 +174,17 @@ def main(self, reporter=None, runner=None): # check out if used options makes any sense args = self.config.args - - sshhosts = [HostInfo('localhost')] # this is just an info to reporter - + + hm = HostManager(self.config, hosts=[HostInfo('localhost')]) + hosts = hm.hosts if not self.config.option.nomagic: py.magic.invoke(assertion=1) reporter, startserverflag = self.init_reporter(reporter, - sshhosts, LocalReporter, args[0]) + hosts, LocalReporter, args[0]) reporter, checkfun = self.wrap_reporter(reporter) - reporter(repevent.TestStarted(sshhosts)) + reporter(repevent.TestStarted(hosts)) colitems = self.config.getcolitems() reporter(repevent.RsyncFinished()) Modified: py/trunk/py/test/rsession/testing/test_hostmanage.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_hostmanage.py (original) +++ py/trunk/py/test/rsession/testing/test_hostmanage.py Mon Feb 5 01:14:11 2007 @@ -109,11 +109,17 @@ assert not res2 class TestHostManager(DirSetup): + def test_hostmanager_custom_hosts(self): + config = py.test.config._reparse([self.source]) + hm = HostManager(config, hosts=[1,2,3]) + assert hm.hosts == [1,2,3] + def test_hostmanager_init_rsync_topdir(self): dir2 = self.source.ensure("dir1", "dir2", dir=1) dir2.ensure("hello") config = py.test.config._reparse([self.source]) - hm = HostManager([HostInfo("localhost:" + str(self.dest))], config) + hm = HostManager(config, + hosts=[HostInfo("localhost:" + str(self.dest))]) events = [] hm.init_rsync(reporter=events.append) assert self.dest.join("dir1").check() @@ -128,7 +134,8 @@ dist_rsync_roots = ['dir1/dir2'] """)) config = py.test.config._reparse([self.source]) - hm = HostManager([HostInfo("localhost:" + str(self.dest))], config) + hm = HostManager(config, + hosts=[HostInfo("localhost:" + str(self.dest))]) events = [] hm.init_rsync(reporter=events.append) assert self.dest.join("dir1").check() Modified: py/trunk/py/test/rsession/testing/test_rsession.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_rsession.py (original) +++ py/trunk/py/test/rsession/testing/test_rsession.py Mon Feb 5 01:14:11 2007 @@ -121,7 +121,7 @@ teardown_events = [] tmpdir = py.test.ensuretemp("emptyconftest") config = py.test.config._reparse([tmpdir]) - hm = HostManager(hosts, config) + hm = HostManager(config, hosts) nodes = hm.init_hosts(setup_events.append) hm.teardown_hosts(teardown_events.append, [node.channel for node in nodes], nodes) @@ -146,7 +146,7 @@ allevents = [] config = py.test.config._reparse([]) - hm = HostManager(hosts, config) + hm = HostManager(config, hosts=hosts) nodes = hm.init_hosts(allevents.append) from py.__.test.rsession.testing.test_executor \ From hpk at codespeak.net Mon Feb 5 01:23:16 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 5 Feb 2007 01:23:16 +0100 (CET) Subject: [py-svn] r37946 - in py/trunk/py/test/rsession: . testing Message-ID: <20070205002316.2D2FE10074@code0.codespeak.net> Author: hpk Date: Mon Feb 5 01:23:14 2007 New Revision: 37946 Modified: py/trunk/py/test/rsession/hostmanage.py py/trunk/py/test/rsession/rsession.py py/trunk/py/test/rsession/testing/test_rsession.py Log: setup/teardown hosts correspond Modified: py/trunk/py/test/rsession/hostmanage.py ============================================================================== --- py/trunk/py/test/rsession/hostmanage.py (original) +++ py/trunk/py/test/rsession/hostmanage.py Mon Feb 5 01:23:14 2007 @@ -127,12 +127,8 @@ finishedcallback=donecallback) rsync.send(root) - def init_hosts(self, reporter): - # hosts ready + def setup_hosts(self, reporter): self.init_rsync(reporter) - return self.setup_nodes(reporter) - - def setup_nodes(self, reporter): nodes = [] for host in self.hosts: if hasattr(host.gw, 'remote_exec'): # otherwise dummy for tests :/ Modified: py/trunk/py/test/rsession/rsession.py ============================================================================== --- py/trunk/py/test/rsession/rsession.py (original) +++ py/trunk/py/test/rsession/rsession.py Mon Feb 5 01:23:14 2007 @@ -133,21 +133,21 @@ reporter(repevent.TestStarted(hm.hosts)) try: - nodes = hm.init_hosts(reporter) + nodes = hm.setup_hosts(reporter) reporter(repevent.RsyncFinished()) try: self.dispatch_tests(nodes, reporter, checkfun) except (KeyboardInterrupt, SystemExit): print >>sys.stderr, "C-c pressed waiting for gateways to teardown..." channels = [node.channel for node in nodes] - hostmanager.kill_channels(channels) - hostmanager.teardown_gateways(reporter, channels) + hm.kill_channels(channels) + hm.teardown_gateways(reporter, channels) print >>sys.stderr, "... Done" raise channels = [node.channel for node in nodes] - hostmanager.teardown_hosts(reporter, channels, nodes, - exitfirst=self.config.option.exitfirst) + hm.teardown_hosts(reporter, channels, nodes, + exitfirst=self.config.option.exitfirst) reporter(repevent.Nodes(nodes)) retval = reporter(repevent.TestFinished()) self.kill_server(startserverflag) Modified: py/trunk/py/test/rsession/testing/test_rsession.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_rsession.py (original) +++ py/trunk/py/test/rsession/testing/test_rsession.py Mon Feb 5 01:23:14 2007 @@ -122,7 +122,7 @@ tmpdir = py.test.ensuretemp("emptyconftest") config = py.test.config._reparse([tmpdir]) hm = HostManager(config, hosts) - nodes = hm.init_hosts(setup_events.append) + nodes = hm.setup_hosts(setup_events.append) hm.teardown_hosts(teardown_events.append, [node.channel for node in nodes], nodes) @@ -147,7 +147,7 @@ config = py.test.config._reparse([]) hm = HostManager(config, hosts=hosts) - nodes = hm.init_hosts(allevents.append) + nodes = hm.setup_hosts(allevents.append) from py.__.test.rsession.testing.test_executor \ import ItemTestPassing, ItemTestFailing, ItemTestSkipping From hpk at codespeak.net Mon Feb 5 01:35:42 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 5 Feb 2007 01:35:42 +0100 (CET) Subject: [py-svn] r37947 - in py/trunk/py/path/local: . testing Message-ID: <20070205003542.4A98910074@code0.codespeak.net> Author: hpk Date: Mon Feb 5 01:35:40 2007 New Revision: 37947 Modified: py/trunk/py/path/local/local.py py/trunk/py/path/local/testing/test_local.py Log: added _gethomedir() helper to get at homedirectory Modified: py/trunk/py/path/local/local.py ============================================================================== --- py/trunk/py/path/local/local.py (original) +++ py/trunk/py/path/local/local.py Mon Feb 5 01:35:40 2007 @@ -537,6 +537,15 @@ pass return None sysfind = classmethod(sysfind) + + def _gethomedir(cls): + try: + x = os.environ['HOME'] + except KeyError: + x = os.environ['HOMEPATH'] + return cls(x) + _gethomedir = classmethod(_gethomedir) + #""" #special class constructors for local filesystem paths #""" Modified: py/trunk/py/path/local/testing/test_local.py ============================================================================== --- py/trunk/py/path/local/testing/test_local.py (original) +++ py/trunk/py/path/local/testing/test_local.py Mon Feb 5 01:35:40 2007 @@ -332,6 +332,10 @@ assert pkg.pypkgpath() == pkg assert pkg.join('subdir', '__init__.py').pypkgpath() == pkg +def test_homedir(): + homedir = py.path.local._gethomedir() + assert homedir.check(dir=1) + #class XTestLocalPath(TestLocalPath): # def __init__(self): # TestLocalPath.__init__(self) From hpk at codespeak.net Mon Feb 5 01:36:03 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 5 Feb 2007 01:36:03 +0100 (CET) Subject: [py-svn] r37948 - in py/trunk/py/test/rsession: . testing Message-ID: <20070205003603.739F01007A@code0.codespeak.net> Author: hpk Date: Mon Feb 5 01:36:00 2007 New Revision: 37948 Modified: py/trunk/py/test/rsession/hostmanage.py py/trunk/py/test/rsession/testing/test_hostmanage.py Log: fix for win32 Modified: py/trunk/py/test/rsession/hostmanage.py ============================================================================== --- py/trunk/py/test/rsession/hostmanage.py (original) +++ py/trunk/py/test/rsession/hostmanage.py Mon Feb 5 01:36:00 2007 @@ -41,7 +41,9 @@ import os targetdir = %r if not os.path.isabs(targetdir): - homedir = os.environ['HOME'] + homedir = os.environ.get('HOME', '') + if not homedir: + homedir = os.environ.get('HOMEPATH', '.') targetdir = os.path.join(homedir, targetdir) if not os.path.exists(targetdir): os.makedirs(targetdir) Modified: py/trunk/py/test/rsession/testing/test_hostmanage.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_hostmanage.py (original) +++ py/trunk/py/test/rsession/testing/test_hostmanage.py Mon Feb 5 01:36:00 2007 @@ -43,7 +43,7 @@ x.initgateway() assert x.gw try: - homedir = py.path.local(py.std.os.environ['HOME']) + homedir = py.path.local._gethomedir() expected = homedir.join(name) assert x.gw_remotepath == str(expected) assert x.localdest == expected From hpk at codespeak.net Mon Feb 5 01:47:44 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 5 Feb 2007 01:47:44 +0100 (CET) Subject: [py-svn] r37949 - py/trunk/py/io/test Message-ID: <20070205004744.1273B10072@code0.codespeak.net> Author: hpk Date: Mon Feb 5 01:47:43 2007 New Revision: 37949 Modified: py/trunk/py/io/test/test_stdcapture.py Log: fix test for nocapturing output runs (and --pdb) Modified: py/trunk/py/io/test/test_stdcapture.py ============================================================================== --- py/trunk/py/io/test/test_stdcapture.py (original) +++ py/trunk/py/io/test/test_stdcapture.py Mon Feb 5 01:47:43 2007 @@ -123,14 +123,18 @@ assert err.startswith("4") def test_capture_no_sys(): - cap = py.io.StdCaptureFD(patchsys=False) - print >>sys.stdout, "hello" - print >>sys.stderr, "world" - os.write(1, "1") - os.write(2, "2") - out, err = cap.reset() - assert out == "1" - assert err == "2" + capsys = py.io.StdCapture() + try: + cap = py.io.StdCaptureFD(patchsys=False) + print >>sys.stdout, "hello" + print >>sys.stderr, "world" + os.write(1, "1") + os.write(2, "2") + out, err = cap.reset() + assert out == "1" + assert err == "2" + finally: + capsys.reset() def test_callcapture_nofd(): def func(x, y): From hpk at codespeak.net Mon Feb 5 02:14:18 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 5 Feb 2007 02:14:18 +0100 (CET) Subject: [py-svn] r37950 - in py/trunk/py: doc test test/rsession test/rsession/testing Message-ID: <20070205011418.9022B10072@code0.codespeak.net> Author: hpk Date: Mon Feb 5 02:14:17 2007 New Revision: 37950 Modified: py/trunk/py/doc/test.txt py/trunk/py/test/defaultconftest.py py/trunk/py/test/rsession/hostmanage.py py/trunk/py/test/rsession/testing/test_hostmanage.py Log: * add and document dist_rsync_ignore option to ignore files and directories for rsyncing Modified: py/trunk/py/doc/test.txt ============================================================================== --- py/trunk/py/doc/test.txt (original) +++ py/trunk/py/doc/test.txt Mon Feb 5 02:14:17 2007 @@ -693,6 +693,7 @@ * `dist_hosts`: a required list of host specifications * `dist_rsync_roots` - a list of relative locations to copy to the remote machines. +* `dist_rsync_ignore` - a list of relative locations to ignore for rsyncing * `dist_remotepython` - the remote python executable to run. * `dist_nicelevel` - process priority of remote nodes. * `dist_boxing` - will run each single test in a separate process Modified: py/trunk/py/test/defaultconftest.py ============================================================================== --- py/trunk/py/test/defaultconftest.py (original) +++ py/trunk/py/test/defaultconftest.py Mon Feb 5 02:14:17 2007 @@ -25,6 +25,7 @@ else: dist_nicelevel = 0 _dist_import_pypy = False # used for regenerating JS application +dist_rsync_ignore = [] # =================================================== Modified: py/trunk/py/test/rsession/hostmanage.py ============================================================================== --- py/trunk/py/test/rsession/hostmanage.py (original) +++ py/trunk/py/test/rsession/hostmanage.py Mon Feb 5 02:14:17 2007 @@ -72,6 +72,10 @@ """ def __init__(self, *args, **kwargs): self._synced = {} + ignores= None + if 'ignores' in kwargs: + ignores = kwargs.pop('ignores') + self._ignores = ignores or [] super(HostRSync, self).__init__(*args, **kwargs) def filter(self, path): @@ -79,7 +83,11 @@ if not path.ext in ('.pyc', '.pyo'): if not path.basename.endswith('~'): if path.check(dotfile=0): - return True + for x in self._ignores: + if path == x: + break + else: + return True def add_target_host(self, host, destrelpath=None, finishedcallback=None): key = host.hostname, host.relpath @@ -115,10 +123,11 @@ def init_rsync(self, reporter): # send each rsync root roots = self.config.getvalue_pathlist("dist_rsync_roots") + ignores = self.config.getvalue_pathlist("dist_rsync_ignore") if roots is None: roots = [self.config.topdir] self.prepare_gateways() - rsync = HostRSync() + rsync = HostRSync(ignores=ignores) for root in roots: destrelpath = root.relto(self.config.topdir) for host in self.hosts: Modified: py/trunk/py/test/rsession/testing/test_hostmanage.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_hostmanage.py (original) +++ py/trunk/py/test/rsession/testing/test_hostmanage.py Mon Feb 5 02:14:17 2007 @@ -142,3 +142,21 @@ assert self.dest.join("dir1", "dir2").check() assert self.dest.join("dir1", "dir2", 'hello').check() assert not self.dest.join("bogus").check() + + def test_hostmanager_rsync_ignore(self): + dir2 = self.source.ensure("dir1", "dir2", dir=1) + dir5 = self.source.ensure("dir5", "dir6", "bogus") + dirf = self.source.ensure("dir5", "file") + dir2.ensure("hello") + self.source.join("conftest.py").write(py.code.Source(""" + dist_rsync_ignore = ['dir1/dir2', 'dir5/dir6'] + """)) + config = py.test.config._reparse([self.source]) + hm = HostManager(config, + hosts=[HostInfo("localhost:" + str(self.dest))]) + events = [] + hm.init_rsync(reporter=events.append) + assert self.dest.join("dir1").check() + assert not self.dest.join("dir1", "dir2").check() + assert self.dest.join("dir5","file").check() + assert not self.dest.join("dir6").check() From hpk at codespeak.net Mon Feb 5 13:55:33 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 5 Feb 2007 13:55:33 +0100 (CET) Subject: [py-svn] r37960 - in py/trunk/py/execnet: . testing Message-ID: <20070205125533.5B9BD1007D@code0.codespeak.net> Author: hpk Date: Mon Feb 5 13:55:31 2007 New Revision: 37960 Modified: py/trunk/py/execnet/rsync.py py/trunk/py/execnet/testing/test_rsync.py Log: refactored the tests and added tests and code for disallowing to send() twice without adding new targets. Modified: py/trunk/py/execnet/rsync.py ============================================================================== --- py/trunk/py/execnet/rsync.py (original) +++ py/trunk/py/execnet/rsync.py Mon Feb 5 13:55:31 2007 @@ -28,18 +28,6 @@ def filter(self, path): return True - def add_target(self, gateway, destdir, finishedcallback=None): - """ Adds a remote target specified via a 'gateway' - and a remote destination directory. - """ - assert finishedcallback is None or callable(finishedcallback) - def itemcallback(req): - self._receivequeue.put((channel, req)) - channel = gateway.remote_exec(REMOTE_SOURCE) - channel.setcallback(itemcallback, endmarker = None) - channel.send((str(destdir), self._options)) - self._channels[channel] = finishedcallback - def _end_of_channel(self, channel): if channel in self._channels: # too early! we must have got an error @@ -104,6 +92,9 @@ def send(self, sourcedir): """ Sends a sourcedir to all added targets. """ + if not self._channels: + raise IOError("no targets available, maybing you " + "are trying call send() twice?") self._sourcedir = str(sourcedir) # normalize a trailing '/' away self._sourcedir = os.path.dirname(os.path.join(self._sourcedir, 'x')) @@ -137,6 +128,19 @@ else: assert "Unknown command %s" % command + def add_target(self, gateway, destdir, finishedcallback=None): + """ Adds a remote target specified via a 'gateway' + and a remote destination directory. + """ + assert finishedcallback is None or callable(finishedcallback) + def itemcallback(req): + self._receivequeue.put((channel, req)) + channel = gateway.remote_exec(REMOTE_SOURCE) + channel.setcallback(itemcallback, endmarker = None) + channel.send((str(destdir), self._options)) + self._channels[channel] = finishedcallback + + def _broadcast(self, msg): for channel in self._channels: channel.send(msg) Modified: py/trunk/py/execnet/testing/test_rsync.py ============================================================================== --- py/trunk/py/execnet/testing/test_rsync.py (original) +++ py/trunk/py/execnet/testing/test_rsync.py Mon Feb 5 13:55:31 2007 @@ -11,92 +11,114 @@ mod.gw2.exit() -def test_dirsync(): - base = py.test.ensuretemp('dirsync') - dest = base.join('dest') - dest2 = base.join('dest2') - source = base.mkdir('source') +class DirSetup: + def setup_method(self, method): + name = "%s.%s" %(self.__class__.__name__, method.func_name) + self.tmpdir = t = py.test.ensuretemp(name) + self.source = t.join("source") + self.dest1 = t.join("dest1") + self.dest2 = t.join("dest2") - for s in ('content1', 'content2-a-bit-longer'): - source.ensure('subdir', 'file1').write(s) +class TestRSync(DirSetup): + def test_notargets(self): + rsync = RSync() + py.test.raises(IOError, "rsync.send(self.source)") + + def test_dirsync(self): + dest = self.dest1 + dest2 = self.dest2 + source = self.source + + for s in ('content1', 'content2-a-bit-longer'): + source.ensure('subdir', 'file1').write(s) + rsync = RSync() + rsync.add_target(gw, dest) + rsync.add_target(gw2, dest2) + rsync.send(source) + assert dest.join('subdir').check(dir=1) + assert dest.join('subdir', 'file1').check(file=1) + assert dest.join('subdir', 'file1').read() == s + assert dest2.join('subdir').check(dir=1) + assert dest2.join('subdir', 'file1').check(file=1) + assert dest2.join('subdir', 'file1').read() == s + + source.join('subdir').remove('file1') rsync = RSync() - rsync.add_target(gw, dest) rsync.add_target(gw2, dest2) + rsync.add_target(gw, dest) rsync.send(source) - assert dest.join('subdir').check(dir=1) assert dest.join('subdir', 'file1').check(file=1) - assert dest.join('subdir', 'file1').read() == s - assert dest2.join('subdir').check(dir=1) assert dest2.join('subdir', 'file1').check(file=1) - assert dest2.join('subdir', 'file1').read() == s - - source.join('subdir').remove('file1') - rsync = RSync() - rsync.add_target(gw2, dest2) - rsync.add_target(gw, dest) - rsync.send(source) - assert dest.join('subdir', 'file1').check(file=1) - assert dest2.join('subdir', 'file1').check(file=1) - rsync = RSync(delete=True) - rsync.add_target(gw2, dest2) - rsync.add_target(gw, dest) - rsync.send(source) - assert not dest.join('subdir', 'file1').check() - assert not dest2.join('subdir', 'file1').check() - -def test_symlink_rsync(): - if py.std.sys.platform == 'win32': - py.test.skip("symlinks are unsupported on Windows.") - base = py.test.ensuretemp('symlinkrsync') - dest = base.join('dest') - source = base.join('source') - source.ensure("existant") - source.join("rellink").mksymlinkto(source.join("existant"), absolute=0) - source.join('abslink').mksymlinkto(source.join("existant")) - - rsync = RSync() - rsync.add_target(gw, dest) - rsync.send(source) - - assert dest.join('rellink').readlink() == dest.join("existant") - assert dest.join('abslink').readlink() == dest.join("existant") - -def test_callback(): - base = py.test.ensuretemp('callback') - dest = base.join("dest") - source = base.join("source") - source.ensure("existant").write("a" * 100) - source.ensure("existant2").write("a" * 10) - total = {} - def callback(cmd, lgt, channel): - total[(cmd, lgt)] = True - - rsync = RSync(callback=callback) - #rsync = RSync() - rsync.add_target(gw, dest) - rsync.send(source) - - assert total == {("list", 110):True, ("ack", 100):True, ("ack", 10):True} - -def test_file_disappearing(): - base = py.test.ensuretemp("file_disappearing") - dest = base.join("dest") - source = base.join("source") - source.ensure("ex").write("a" * 100) - source.ensure("ex2").write("a" * 100) - - class DRsync(RSync): - def filter(self, x): - assert x != source - if x.endswith("ex2"): - self.x = 1 - source.join("ex2").remove() - return True - - rsync = DRsync() - rsync.add_target(gw, dest) - rsync.send(source) - assert rsync.x == 1 - assert len(dest.listdir()) == 1 - assert len(source.listdir()) == 1 - + rsync = RSync(delete=True) + rsync.add_target(gw2, dest2) + rsync.add_target(gw, dest) + rsync.send(source) + assert not dest.join('subdir', 'file1').check() + assert not dest2.join('subdir', 'file1').check() + + def test_dirsync_twice(self): + source = self.source + source.ensure("hello") + rsync = RSync() + rsync.add_target(gw, self.dest1) + rsync.send(self.source) + assert self.dest1.join('hello').check() + py.test.raises(IOError, "rsync.send(self.source)") + rsync.add_target(gw, self.dest2) + rsync.send(self.source) + assert self.dest2.join('hello').check() + py.test.raises(IOError, "rsync.send(self.source)") + + def test_symlink_rsync(self): + if py.std.sys.platform == 'win32': + py.test.skip("symlinks are unsupported on Windows.") + source = self.source + dest = self.dest1 + self.source.ensure("existant") + source.join("rellink").mksymlinkto(source.join("existant"), absolute=0) + source.join('abslink').mksymlinkto(source.join("existant")) + + rsync = RSync() + rsync.add_target(gw, dest) + rsync.send(source) + + assert dest.join('rellink').readlink() == dest.join("existant") + assert dest.join('abslink').readlink() == dest.join("existant") + + def test_callback(self): + dest = self.dest1 + source = self.source + source.ensure("existant").write("a" * 100) + source.ensure("existant2").write("a" * 10) + total = {} + def callback(cmd, lgt, channel): + total[(cmd, lgt)] = True + + rsync = RSync(callback=callback) + #rsync = RSync() + rsync.add_target(gw, dest) + rsync.send(source) + + assert total == {("list", 110):True, ("ack", 100):True, ("ack", 10):True} + + def test_file_disappearing(self): + dest = self.dest1 + source = self.source + source.ensure("ex").write("a" * 100) + source.ensure("ex2").write("a" * 100) + + class DRsync(RSync): + def filter(self, x): + assert x != source + if x.endswith("ex2"): + self.x = 1 + source.join("ex2").remove() + return True + + rsync = DRsync() + rsync.add_target(gw, dest) + rsync.send(source) + assert rsync.x == 1 + assert len(dest.listdir()) == 1 + assert len(source.listdir()) == 1 + From fijal at codespeak.net Mon Feb 5 16:11:07 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Mon, 5 Feb 2007 16:11:07 +0100 (CET) Subject: [py-svn] r37971 - py/trunk/py/test/rsession Message-ID: <20070205151107.2BA1C1007F@code0.codespeak.net> Author: fijal Date: Mon Feb 5 16:11:05 2007 New Revision: 37971 Modified: py/trunk/py/test/rsession/hostmanage.py Log: This was there for a reason that we don't want to have different pylib loaded (there was even test for that, apparently killed) Modified: py/trunk/py/test/rsession/hostmanage.py ============================================================================== --- py/trunk/py/test/rsession/hostmanage.py (original) +++ py/trunk/py/test/rsession/hostmanage.py Mon Feb 5 16:11:05 2007 @@ -5,7 +5,8 @@ from py.__.test.rsession.master import MasterNode from py.__.test.rsession.slave import setup_slave -from py.__.test.rsession import repevent +from py.__.test.rsession import repevent +from py.__.execnet.register import PopenCmdGateway class HostInfo(object): """ Class trying to store all necessary attributes @@ -31,8 +32,9 @@ def initgateway(self, python="python"): assert not hasattr(self, 'gw') - if self.hostname == "localhost": - gw = py.execnet.PopenGateway(python=python) + if self.hostname == "localhost": + cmd = 'cd ~; %s -u -c "exec input()"' % python + gw = PopenCmdGateway(cmd) else: gw = py.execnet.SshGateway(self.hostname, remotepython=python) @@ -76,6 +78,7 @@ if 'ignores' in kwargs: ignores = kwargs.pop('ignores') self._ignores = ignores or [] + kwargs['delete'] = True super(HostRSync, self).__init__(*args, **kwargs) def filter(self, path): From guido at codespeak.net Mon Feb 5 23:37:25 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Mon, 5 Feb 2007 23:37:25 +0100 (CET) Subject: [py-svn] r37984 - in py/trunk/py/doc: . apigen Message-ID: <20070205223725.97FB01007E@code0.codespeak.net> Author: guido Date: Mon Feb 5 23:37:23 2007 New Revision: 37984 Removed: py/trunk/py/doc/apigen/ Modified: py/trunk/py/doc/apigen.txt Log: Moved all the apigen information into one document and adjusted to match the current situation. Modified: py/trunk/py/doc/apigen.txt ============================================================================== --- py/trunk/py/doc/apigen.txt (original) +++ py/trunk/py/doc/apigen.txt Mon Feb 5 23:37:23 2007 @@ -1,36 +1,144 @@ +=========================================== apigen - API documentation generation tool =========================================== What is it? ------------- +=========== Apigen is a tool for automatically generating API reference documentation for -Python projects. It works by examining code at runtime rather than at -compile time. This way it is capable of displaying information -about the code base after initialization. A drawback is that -you cannot easily document source code that automatically -starts server processes or has some other irreversible effects upon getting imported. - -The apigen functionality can either be used from code, or from -py.test, in the latter case it will gather information about -modules, classes to export explicitely by using provided script. - -Please note that apigen is currently geared towards documenting the -py library itself, making it nicely work for other projects -may still require a bit of adaption and refinement work. - -Using from code ----------------- - -The library provides a simple API to generate a py.rest.rst tree (which -represents a ReStructuredText document), along with some helper classes to -control the output. The most important objects are the Tracer, which traces -code execution by registering itself with sys.settrace, the DocStorage class, -that stores Tracer information, and the RestGen class which creates a ReST -tree (see py.rest.rst). +Python projects. It works by examining code at runtime rather than at compile +time. This way it is capable of displaying information about the code base +after initialization. A drawback is that you cannot easily document source code +that automatically starts server processes or has some other irreversible +effects upon getting imported. + +The apigen functionality is normally triggered from :api:`py.test`, and while +running the tests it gathers information such as code paths, arguments and +return values of callables, and exceptions that can be raised while the code +runs (XXX not yet!) to include in the documentation. It's also possible to +run the tracer (which collects the data) in other code if your project +does not use :api:`py.test` but still wants to collect the runtime information +and build the docs. + +Apigen is written for the :api:`py` lib, but can be used to build documentation +for any project: there are hooks in py.test to, by providing a simple script, +build api documentation for the tested project when running py.test. Of course +this does imply :api:`py.test` is actually used: if little or no tests are +actually ran, the additional information (code paths, arguments and return +values and exceptions) can not be gathered and thus there will be less of an +advantage of apigen compared to other solutions. + +Features +======== + +Some features were mentioned above already, but here's a complete list of all +the niceties apigen has to offer: + + * source documents + + Apigen not only builds the API documentation, but also a tree of + syntax-colored source files, with links from the API docs to the source + files. + + * abundance of information + + compared to other documentation generation tools, apigen produces an + abundant amount of information: it provides syntax-colored code snippets, + code path traces, etc. + + * linking + + besides links to the source files, apigen provides links all across the + documentation: callable arguments and return values link to their + definition (if part of the documented code), class definition to their + base classes (again, if they're part of the documented code), and + everywhere are links to the source files (including in traces) + + * (hopefully) improves testing + + because the documentation is built partially from test results, developers + may (especially if they're using the documentation themselves) be more + aware of untested parts of the code, or parts can use more tests or need + attention + +Using apigen +============ + +To trigger apigen, all you need to do is run the :source:`py/bin/py.test` tool +with an --apigen argument, as such:: + + $ py.test --apigen= + +where is a path to a script containing some special hooks to build +the documents (see below). The script to build the documents for the :api:`py` +lib can be found in :source:`py/apigen/apigen.py`, so building those documents +can be done by cd'ing to the 'py' directory, and executing:: + + $ py.test --apigen=apigen/apigen.py + +The documents will by default be built in the *parent directory* of the +*package dir* (in this case the 'py' directory). Be careful that you don't +overwrite anything! + +Other projects +============== + +To use apigen from another project, there are three things that you need to do: + +Use :api:`py.test` for unit tests +--------------------------------- + +This is a good idea anyway... ;) The more tests, the more tracing information +and such can be built, so it makes sense to have good test coverage when using +this tool. + +Provide :api:`py.test` hooks +---------------------------- + +To hook into the unit testing framework, you will need to write a script with +two functions. The first should be called 'get_documentable_items', gets a +package dir (the root of the project) as argument, and should return a tuple +with the package name as first element, and a dict as second. The dict should +contain, for all the to-be-documented items, a dotted name as key and a +reference to the item as value. + +The second function should be called 'build', and gets also the package dir as +argument, but also a reference to a DocStorageAcessor, which contains +information gathered by the tracer, and a reference to a +:api:`py.io.StdCaptureFD` instance that is used to capture stdout and stderr, +and allows writing to them, when the docs are built. + +This 'build' function is responsible for actually building the documentation, +and, depending on your needs, can be used to control each aspect of it. In most +situations you will just copy the code from :source:`py/apigen/apigen.py`'s +build() function, but if you want you can choose to build entirely different +output formats by directly accessing the DocStorageAccessor class. + +Provide layout +-------------- + +For the :api:`py` lib tests, the 'LayoutPage' class found in +:source:`py/apigen/layout.py` is used, which produces HTML specific for that +particular library (with a menubar, etc.). To customize this, you will need to +provide a similar class, most probably using the Page base class from +:source:`py/doc/confrest.py`. Note that this step depends on how heavy the +customization in the previous step is done: if you decide to directly use the +DocStorageAccessor rather than let the code in :source:`py/apigen/htmlgen.py` +build HTML for you, this can be skipped. + +Using apigen from code +====================== + +If you want to avoid using :api:`py.test`, or have an other idea of how to best +collect information while running code, the apigen functionality can be +directly accessed. The most important classes are the Tracer class found in +:source:`py/apigen/tracer/tracer.py`, which holds the information gathered +during the tests, and the DocStorage and DocStorageAccessor classes from +:source:`py/apigen/tracer/docstorage.py`, which (respectively) store the data, +and make it accessible. Gathering information -++++++++++++++++++++++ +--------------------- To gather information about documentation, you will first need to tell the tool what objects it should investigate. Only information for registered objects @@ -50,71 +158,24 @@ >>> t.end_tracing() Now the 'ds' variable should contain all kinds of information about both the -py.path.local and the py.path.svnwc class (it will walk through 'toregister' to -find information about all it contains), and things like call stacks, and -possible argument types, etc. as additional information about -py.path.local.check() (since it was called from the traced code). - -Viewing information -++++++++++++++++++++ - -Viewing the information stored in the DocStorage instance isn't very hard -either. As explained there is a RestGen class that creates a py.rest.rst tree, -which can directly be serialized to ReStructuredText, which can in turn be -converted to other formats. Also the py.rest.rst tree can be manipulated -directly, or converted to formats other than ReST (currently only HTML) using -special transformers. - -There are several helper classes available that wrap the output format -generation. There are two types of helper classes, 'LinkWriters' and 'Writers'. -The first are responsible for generating external links (for viewing source), -the second for generating the actual output from the py.rest.rst tree, and -for generating internal links (which is directly related to generating output). -Instances of these classes are passed to the RestGen class as arguments on -instantiation. - -An example of creating a directory with seperate ReST files (using DirWriter) -from the 'ds' DocumentStorage instance we created below, without any external -links (using DirectPaste). -:: - - >>> from py.__.apigen.rest.genrest import RestGen, DirectPaste, DirWriter - >>> # create a temp dir in /tmp/pytest- - >>> tempdir = py.test.ensuretemp('apigen_example') - >>> rg = RestGen(DocStorageAccessor(ds), DirectPaste(), DirWriter(tempdir)) - >>> rg.write() - -An example of a somewhat more 'real-life' use case, writing to a directory of -HTML files (this uses py.rest.transform), generating links to ViewVC source -views:: - - >>> from py.__.apigen.rest.genrest import ViewVC, HTMLDirWriter - >>> from py.__.apigen.rest.htmlhandlers import HTMLHandler - >>> tempdir = py.test.ensuretemp('apigen_example_2') - >>> rg = RestGen(DocStorageAccessor(ds), ViewVC('http://some.host.com/viewvc/myproj/trunk/'), - ... HTMLDirWriter(HTMLHandler, HTMLHandler, tempdir)) - >>> rg.write() - -Using from py.test -------------------- - -Running unit tests forms an ideal opportunity for apigen to find out about what -happens when code is executed (assuming you have proper test coverage ;). There -are hooks built into py.test that allow you to do that: - -* Write down a python script which contains at least two functions - - - `get_documentable_items() -> {}` - function which will return dictionary - of name to object of exported items - - - `build(pkgpath, docstorageaccessor)` - function which will be invoked afterwards - with DocStorageAccessor instance as an argument (you should read DocStorageAccessor - interface to know how you can access it) +:api:`py.path.local` and the :api:`py.path.svnwc` classes, and things like call +stacks, possible argument types, etc. as additional information about +:api:`py.path.local.check()` (since it was called from the traced code). + +Using the information +--------------------- + +To use the information, we need to get a DocStorageAccessor instance to +provide access to the data stored in the DocStorage object:: + + >>> dsa = DocStorageAccessor(ds) -XXX: Write down some example usage after guido implement the script +Currently there is no API reference available for this object, so you'll have +to read the source (:source:`py/apigen/tracer/docstorage.py`) to see what +functionality it offers. Comparison with other documentation generation tools ----------------------------------------------------- +==================================================== Apigen is of course not the only documentation generation tool available for Python. Although we knew in advance that our tool had certain features the @@ -122,7 +183,7 @@ proper comparison. Tools examined -++++++++++++++ +-------------- After some 'googling around', it turned out that the amount of documentation generation tools available was surprisingly low. There were only 5 packages @@ -179,7 +240,7 @@ * written for Twisted, but quite nice output with other applications Quick overview lists of the other tools -+++++++++++++++++++++++++++++++++++++++ +--------------------------------------- HappyDoc ~~~~~~~~ @@ -217,7 +278,7 @@ widely used it can not be ignored... Questions, remarks, etc. -------------------------- +======================== For more information, questions, remarks, etc. see http://codespeak.net/py. This website also contains links to mailing list and IRC channel. From hpk at codespeak.net Mon Feb 5 23:46:33 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 5 Feb 2007 23:46:33 +0100 (CET) Subject: [py-svn] r37985 - in py/trunk/py/test/rsession: . testing Message-ID: <20070205224633.E25C51007F@code0.codespeak.net> Author: hpk Date: Mon Feb 5 23:46:31 2007 New Revision: 37985 Modified: py/trunk/py/test/rsession/hostmanage.py py/trunk/py/test/rsession/testing/test_hostmanage.py Log: make sure that host.initgateway() will always have the other side chdir()ed to home, thus generalizing 37971 with respect to platform support and making it independent from gateway implementation details. Modified: py/trunk/py/test/rsession/hostmanage.py ============================================================================== --- py/trunk/py/test/rsession/hostmanage.py (original) +++ py/trunk/py/test/rsession/hostmanage.py Mon Feb 5 23:46:31 2007 @@ -6,7 +6,6 @@ from py.__.test.rsession.slave import setup_slave from py.__.test.rsession import repevent -from py.__.execnet.register import PopenCmdGateway class HostInfo(object): """ Class trying to store all necessary attributes @@ -33,24 +32,22 @@ def initgateway(self, python="python"): assert not hasattr(self, 'gw') if self.hostname == "localhost": - cmd = 'cd ~; %s -u -c "exec input()"' % python - gw = PopenCmdGateway(cmd) + gw = py.execnet.PopenGateway(python=python) else: gw = py.execnet.SshGateway(self.hostname, remotepython=python) self.gw = gw - channel = gw.remote_exec(""" + channel = gw.remote_exec(py.code.Source(gethomedir, """ import os targetdir = %r + homedir = gethomedir() if not os.path.isabs(targetdir): - homedir = os.environ.get('HOME', '') - if not homedir: - homedir = os.environ.get('HOMEPATH', '.') targetdir = os.path.join(homedir, targetdir) if not os.path.exists(targetdir): os.makedirs(targetdir) - channel.send(os.path.abspath(targetdir)) - """ % self.relpath) + os.chdir(homedir) + channel.send(targetdir) + """ % self.relpath)) self.gw_remotepath = channel.receive() #print "initialized", gw, "with remotepath", self.gw_remotepath if self.hostname == "localhost": @@ -177,3 +174,10 @@ except: pass channel.gateway.exit() + +def gethomedir(): + import os + homedir = os.environ.get('HOME', '') + if not homedir: + homedir = os.environ.get('HOMEPATH', '.') + return homedir Modified: py/trunk/py/test/rsession/testing/test_hostmanage.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_hostmanage.py (original) +++ py/trunk/py/test/rsession/testing/test_hostmanage.py Mon Feb 5 23:46:31 2007 @@ -37,6 +37,20 @@ py.test.raises((py.process.cmdexec.Error, IOError, EOFError), host.initgateway) + def test_remote_has_homedir_as_currentdir(self): + host = HostInfo("localhost") + old = py.path.local.get_temproot().chdir() + try: + host.initgateway() + channel = host.gw.remote_exec(""" + import os + channel.send(os.getcwd()) + """) + dir = channel.receive() + assert dir == py.path.local._gethomedir() + finally: + old.chdir() + def test_initgateway_localhost_relpath(self): name = "pytestcache-localhost" x = HostInfo("localhost:%s" % name) @@ -160,3 +174,4 @@ assert not self.dest.join("dir1", "dir2").check() assert self.dest.join("dir5","file").check() assert not self.dest.join("dir6").check() + From fijal at codespeak.net Tue Feb 6 00:09:08 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 6 Feb 2007 00:09:08 +0100 (CET) Subject: [py-svn] r37986 - py/trunk/py/test/rsession Message-ID: <20070205230908.B08541007C@code0.codespeak.net> Author: fijal Date: Tue Feb 6 00:09:05 2007 New Revision: 37986 Modified: py/trunk/py/test/rsession/hostmanage.py Log: Add a repr if we have str Add delete keyword to parent Modified: py/trunk/py/test/rsession/hostmanage.py ============================================================================== --- py/trunk/py/test/rsession/hostmanage.py (original) +++ py/trunk/py/test/rsession/hostmanage.py Tue Feb 6 00:09:05 2007 @@ -57,6 +57,9 @@ def __str__(self): return "" % (self.hostname, self.relpath) + def __repr__(self): + return str(self) + def __hash__(self): return hash(self.hostid) @@ -75,8 +78,7 @@ if 'ignores' in kwargs: ignores = kwargs.pop('ignores') self._ignores = ignores or [] - kwargs['delete'] = True - super(HostRSync, self).__init__(*args, **kwargs) + super(HostRSync, self).__init__(delete=True) def filter(self, path): path = py.path.local(path) From cfbolz at codespeak.net Tue Feb 6 00:18:23 2007 From: cfbolz at codespeak.net (cfbolz at codespeak.net) Date: Tue, 6 Feb 2007 00:18:23 +0100 (CET) Subject: [py-svn] r37987 - py/trunk/py/doc Message-ID: <20070205231823.8F7CB1007C@code0.codespeak.net> Author: cfbolz Date: Tue Feb 6 00:18:22 2007 New Revision: 37987 Added: py/trunk/py/doc/impl-test.txt Modified: py/trunk/py/doc/test.txt Log: split the py.test docs into implementation and customization docs and normal user docs. Added: py/trunk/py/doc/impl-test.txt ============================================================================== --- (empty file) +++ py/trunk/py/doc/impl-test.txt Tue Feb 6 00:18:22 2007 @@ -0,0 +1,273 @@ +=============================================== +Implementation and Customization of ``py.test`` +=============================================== + +.. contents:: +.. sectnum:: + + + +.. _`basicpicture`: + + +Collecting and running tests / implementation remarks +====================================================== + +In order to customize ``py.test`` it's good to understand +its basic architure (WARNING: these are not guaranteed +yet to stay the way they are now!):: + + ___________________ + | | + | Collector | + |___________________| + / \ + | Item.run() + | ^ + receive test Items / + | /execute test Item + | / + ___________________/ + | | + | Session | + |___________________| + + ............................. + . conftest.py configuration . + . cmdline options . + ............................. + + +The *Session* basically receives test *Items* from a *Collector*, +and executes them via the ``Item.run()`` method. It monitors +the outcome of the test and reports about failures and successes. + +.. _`collection process`: + +Collectors and the test collection process +------------------------------------------ + +The collecting process is iterative, i.e. the session +traverses and generates a *collector tree*. Here is an example of such +a tree, generated with the command ``py.test --collectonly py/xmlobj``:: + + + + + + + + + + + + + + + + + +By default all directories not starting with a dot are traversed, +looking for ``test_*.py`` and ``*_test.py`` files. Those files +are imported under their `package name`_. + +.. _`collector API`: + +test items are collectors as well +--------------------------------- + +To make the reporting life simple for the session object +items offer a ``run()`` method as well. In fact the session +distinguishes "collectors" from "items" solely by interpreting +their return value. If it is a list, then we recurse into +it, otherwise we consider the "test" as passed. + +.. _`package name`: + +constructing the package name for modules +----------------------------------------- + +Test modules are imported under their fully qualified +name. Given a module ``path`` the fully qualified package +name is constructed as follows: + +* determine the last "upward" directory from ``path`` that + contains an ``__init__.py`` file. Going upwards + means repeatedly calling the ``dirpath()`` method + on a path object (which returns the parent directory + as a path object). + +* insert this base directory into the sys.path list + as its first element + +* import the root package + +* determine the fully qualified name for the module located + at ``path`` ... + + * if the imported root package has a __package__ object + then call ``__package__.getimportname(path)`` + + * otherwise use the relative path of the module path to + the base dir and turn slashes into dots and strike + the trailing ``.py``. + +The Module collector will eventually trigger +``__import__(mod_fqdnname, ...)`` to finally get to +the live module object. + +Side note: this whole logic is performed by local path +object's ``pyimport()`` method. + +Module Collector +----------------- + +The default Module collector looks for test functions +and test classes and methods. Test functions and methods +are prefixed ``test`` by default. Test classes must +start with a capitalized ``Test`` prefix. + + +Customizing the testing process +=============================== + +writing conftest.py files +----------------------------------- + +XXX + +adding custom options ++++++++++++++++++++++++ + +To register a project-specific command line option +you may have the following code within a ``conftest.py`` file:: + + import py + Option = py.test.config.Option + option = py.test.config.addoptions("pypy options", + Option('-V', '--view', action="store_true", dest="view", default=False, + help="view translation tests' flow graphs with Pygame"), + ) + +and you can then access ``option.view`` like this:: + + if option.view: + print "view this!" + +The option will be available if you type ``py.test -h`` +Note that you may only register upper case short +options. ``py.test`` reserves all lower +case short options for its own cross-project usage. + +customizing the collecting and running process +----------------------------------------------- + +To introduce different test items you can create +one or more ``conftest.py`` files in your project. +When the collection process traverses directories +and modules the default collectors will produce +custom Collectors and Items if they are found +in a local ``conftest.py`` file. + +example: perform additional ReST checs +++++++++++++++++++++++++++++++++++++++ + +With your custom collectors or items you can completely +derive from the standard way of collecting and running +tests in a localized manner. Let's look at an example. +If you invoke ``py.test --collectonly py/documentation`` +then you get:: + + + + + + + + + + + + + + + + + ... + +In ``py/documentation/conftest.py`` you find the following +customization:: + + class DocDirectory(py.test.collect.Directory): + + def run(self): + results = super(DocDirectory, self).run() + for x in self.fspath.listdir('*.txt', sort=True): + results.append(x.basename) + return results + + def join(self, name): + if not name.endswith('.txt'): + return super(DocDirectory, self).join(name) + p = self.fspath.join(name) + if p.check(file=1): + return ReSTChecker(p, parent=self) + + Directory = DocDirectory + +The existence of the 'Directory' name in the +``pypy/documentation/conftest.py`` module makes the collection +process defer to our custom "DocDirectory" collector. We extend +the set of collected test items by ``ReSTChecker`` instances +which themselves create ``ReSTSyntaxTest`` and ``LinkCheckerMaker`` +items. All of this instances (need to) follow the `collector API`_. + + +Customizing the collection process in a module +---------------------------------------------- + + REPEATED WARNING: details of the collection and running process are + still subject to refactorings and thus details will change. + If you are customizing py.test at "Item" level then you + definitely want to be subscribed to the `py-dev mailing list`_ + to follow ongoing development. + +If you have a module where you want to take responsibility for +collecting your own test Items and possibly even for executing +a test then you can provide `generative tests`_ that yield +callables and possibly arguments as a tuple. This should +serve some immediate purposes like paramtrized tests. + +.. _`generative tests`: test.html#generative-tests + +The other extension possibility goes deeper into the machinery +and allows you to specify a custom test ``Item`` class which +is responsible for setting up and executing an underlying +test. [XXX not working: You can integrate your custom ``py.test.Item`` subclass +by binding an ``Item`` name to a test class.] Or you can +extend the collection process for a whole directory tree +by putting Items in a ``conftest.py`` configuration file. +The collection process constantly looks at according names +in the *chain of conftest.py* modules to determine collectors +and items at ``Directory``, ``Module``, ``Class``, ``Function`` +or ``Generator`` level. Note that, right now, except for ``Function`` +items all classes are pure collectors, i.e. will return a list +of names (possibly empty). + +XXX implement doctests as alternatives to ``Function`` items. + +Customizing execution of Functions +---------------------------------- + +- Function test items allow total control of executing their + contained test method. ``function.run()`` will get called by the + session in order to actually run a test. The method is responsible + for performing proper setup/teardown ("Test Fixtures") for a + Function test. + +- ``Function.execute(target, *args)`` methods are invoked by + the default ``Function.run()`` to actually execute a python + function with the given (usually empty set of) arguments. + + +.. _`py-dev mailing list`: http://codespeak.net/mailman/listinfo/py-dev Modified: py/trunk/py/doc/test.txt ============================================================================== --- py/trunk/py/doc/test.txt (original) +++ py/trunk/py/doc/test.txt Tue Feb 6 00:18:22 2007 @@ -5,6 +5,12 @@ .. contents:: .. sectnum:: + +This document is about the *usage* of the ``py.test`` testing tool. There is +also document describing the `implementation and the extending of py.test`_. + +.. _`implementation and the extending of py.test`: impl-test.html + starting point: ``py.test`` command line tool ============================================= @@ -40,6 +46,9 @@ subdirectories, starting with the current directory, and run them. Each Python test module is inspected for test methods starting with ``test_``. +.. _`getting started`: getting-started.html + + .. _features: Basic Features of ``py.test`` @@ -94,6 +103,7 @@ `collection process`_ for some implementation details). .. _`generative tests`: +.. _`collection process`: impl-test.html#collection-process generative tests: yielding more tests ------------------------------------- @@ -206,6 +216,10 @@ where in the code the recursion was taking place. You can inhibit traceback "cutting" magic by supplying ``--fulltrace``. +There is also the possibility of usind ``--tb=short`` to get the regular Python +tracebacks (which can sometimes be useful when they are extremely long). Or you +can use ``--tb=no`` to not show any tracebacks at all. + no inheritance requirement -------------------------- @@ -356,270 +370,6 @@ your setup function callable. Did we mention that lazyness is a virtue? -.. _`basicpicture`: - - -Collecting and running tests / implementation remarks -====================================================== - -In order to customize ``py.test`` it's good to understand -its basic architure (WARNING: these are not guaranteed -yet to stay the way they are now!):: - - ___________________ - | | - | Collector | - |___________________| - / \ - | Item.run() - | ^ - receive test Items / - | /execute test Item - | / - ___________________/ - | | - | Session | - |___________________| - - ............................. - . conftest.py configuration . - . cmdline options . - ............................. - - -The *Session* basically receives test *Items* from a *Collector*, -and executes them via the ``Item.run()`` method. It monitors -the outcome of the test and reports about failures and successes. - -.. _`collection process`: - -Collectors and the test collection process ------------------------------------------- - -The collecting process is iterative, i.e. the session -traverses and generates a *collector tree*. Here is an example of such -a tree, generated with the command ``py.test --collectonly py/xmlobj``:: - - - - - - - - - - - - - - - - - -By default all directories not starting with a dot are traversed, -looking for ``test_*.py`` and ``*_test.py`` files. Those files -are imported under their `package name`_. - -.. _`collector API`: - -test items are collectors as well ---------------------------------- - -To make the reporting life simple for the session object -items offer a ``run()`` method as well. In fact the session -distinguishes "collectors" from "items" solely by interpreting -their return value. If it is a list, then we recurse into -it, otherwise we consider the "test" as passed. - -.. _`package name`: - -constructing the package name for modules ------------------------------------------ - -Test modules are imported under their fully qualified -name. Given a module ``path`` the fully qualified package -name is constructed as follows: - -* determine the last "upward" directory from ``path`` that - contains an ``__init__.py`` file. Going upwards - means repeatedly calling the ``dirpath()`` method - on a path object (which returns the parent directory - as a path object). - -* insert this base directory into the sys.path list - as its first element - -* import the root package - -* determine the fully qualified name for the module located - at ``path`` ... - - * if the imported root package has a __package__ object - then call ``__package__.getimportname(path)`` - - * otherwise use the relative path of the module path to - the base dir and turn slashes into dots and strike - the trailing ``.py``. - -The Module collector will eventually trigger -``__import__(mod_fqdnname, ...)`` to finally get to -the live module object. - -Side note: this whole logic is performed by local path -object's ``pyimport()`` method. - -Module Collector ------------------ - -The default Module collector looks for test functions -and test classes and methods. Test functions and methods -are prefixed ``test`` by default. Test classes must -start with a capitalized ``Test`` prefix. - - -Customizing the testing process -=============================== - -writing conftest.py files ------------------------------------ - -XXX - -adding custom options -+++++++++++++++++++++++ - -To register a project-specific command line option -you may have the following code within a ``conftest.py`` file:: - - import py - Option = py.test.config.Option - option = py.test.config.addoptions("pypy options", - Option('-V', '--view', action="store_true", dest="view", default=False, - help="view translation tests' flow graphs with Pygame"), - ) - -and you can then access ``option.view`` like this:: - - if option.view: - print "view this!" - -The option will be available if you type ``py.test -h`` -Note that you may only register upper case short -options. ``py.test`` reserves all lower -case short options for its own cross-project usage. - -customizing the collecting and running process ------------------------------------------------ - -To introduce different test items you can create -one or more ``conftest.py`` files in your project. -When the collection process traverses directories -and modules the default collectors will produce -custom Collectors and Items if they are found -in a local ``conftest.py`` file. - -example: perform additional ReST checs -++++++++++++++++++++++++++++++++++++++ - -With your custom collectors or items you can completely -derive from the standard way of collecting and running -tests in a localized manner. Let's look at an example. -If you invoke ``py.test --collectonly py/documentation`` -then you get:: - - - - - - - - - - - - - - - - - ... - -In ``py/documentation/conftest.py`` you find the following -customization:: - - class DocDirectory(py.test.collect.Directory): - - def run(self): - results = super(DocDirectory, self).run() - for x in self.fspath.listdir('*.txt', sort=True): - results.append(x.basename) - return results - - def join(self, name): - if not name.endswith('.txt'): - return super(DocDirectory, self).join(name) - p = self.fspath.join(name) - if p.check(file=1): - return ReSTChecker(p, parent=self) - - Directory = DocDirectory - -The existence of the 'Directory' name in the -``pypy/documentation/conftest.py`` module makes the collection -process defer to our custom "DocDirectory" collector. We extend -the set of collected test items by ``ReSTChecker`` instances -which themselves create ``ReSTSyntaxTest`` and ``LinkCheckerMaker`` -items. All of this instances (need to) follow the `collector API`_. - - -Customizing the collection process in a module ----------------------------------------------- - - REPEATED WARNING: details of the collection and running process are - still subject to refactorings and thus details will change. - If you are customizing py.test at "Item" level then you - definitely want to be subscribed to the `py-dev mailing list`_ - to follow ongoing development. - -If you have a module where you want to take responsibility for -collecting your own test Items and possibly even for executing -a test then you can provide `generative tests`_ that yield -callables and possibly arguments as a tuple. This should -serve some immediate purposes like paramtrized tests. - -The other extension possibility goes deeper into the machinery -and allows you to specify a custom test ``Item`` class which -is responsible for setting up and executing an underlying -test. [XXX not working: You can integrate your custom ``py.test.Item`` subclass -by binding an ``Item`` name to a test class.] Or you can -extend the collection process for a whole directory tree -by putting Items in a ``conftest.py`` configuration file. -The collection process constantly looks at according names -in the *chain of conftest.py* modules to determine collectors -and items at ``Directory``, ``Module``, ``Class``, ``Function`` -or ``Generator`` level. Note that, right now, except for ``Function`` -items all classes are pure collectors, i.e. will return a list -of names (possibly empty). - -XXX implement doctests as alternatives to ``Function`` items. - -Customizing execution of Functions ----------------------------------- - -- Function test items allow total control of executing their - contained test method. ``function.run()`` will get called by the - session in order to actually run a test. The method is responsible - for performing proper setup/teardown ("Test Fixtures") for a - Function test. - -- ``Function.execute(target, *args)`` methods are invoked by - the default ``Function.run()`` to actually execute a python - function with the given (usually empty set of) arguments. - -.. _`getting started`: getting-started.html -.. _`py-dev mailing list`: http://codespeak.net/mailman/listinfo/py-dev - - Automated Distributed Testing ================================== @@ -710,8 +460,10 @@ dist_maxwait = 100 dist_taskspernode = 10 -Running server is done by ``-w`` command line option or ``--startserver`` -(the former might change at some point due to conflicts). +To use the browser-based reporter (with a nice AJAX interface) you have to tell +``py.test`` to run a small server locally using the ``-w`` or ``--startserver`` +command line options. Afterwards you can point your browser to localhost:8000 +to see the progress of the testing. Development Notes ----------------- From hpk at codespeak.net Tue Feb 6 00:21:35 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 6 Feb 2007 00:21:35 +0100 (CET) Subject: [py-svn] r37988 - py/trunk/py/test/rsession/testing Message-ID: <20070205232135.41B9A1007C@code0.codespeak.net> Author: hpk Date: Tue Feb 6 00:21:33 2007 New Revision: 37988 Modified: py/trunk/py/test/rsession/testing/test_hostmanage.py Log: adding a bit to the test (but the code is already correct) Modified: py/trunk/py/test/rsession/testing/test_hostmanage.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_hostmanage.py (original) +++ py/trunk/py/test/rsession/testing/test_hostmanage.py Tue Feb 6 00:21:33 2007 @@ -142,6 +142,7 @@ def test_hostmanager_init_rsync_rsync_roots(self): dir2 = self.source.ensure("dir1", "dir2", dir=1) + self.source.ensure("dir1", "somefile", dir=1) dir2.ensure("hello") self.source.ensure("bogusdir", "file") self.source.join("conftest.py").write(py.code.Source(""" @@ -156,6 +157,7 @@ assert self.dest.join("dir1", "dir2").check() assert self.dest.join("dir1", "dir2", 'hello').check() assert not self.dest.join("bogus").check() + assert not self.dest.join("dir1", "somefile").check() def test_hostmanager_rsync_ignore(self): dir2 = self.source.ensure("dir1", "dir2", dir=1) From cfbolz at codespeak.net Tue Feb 6 00:50:12 2007 From: cfbolz at codespeak.net (cfbolz at codespeak.net) Date: Tue, 6 Feb 2007 00:50:12 +0100 (CET) Subject: [py-svn] r37989 - py/trunk/py/doc Message-ID: <20070205235012.12DB410077@code0.codespeak.net> Author: cfbolz Date: Tue Feb 6 00:50:11 2007 New Revision: 37989 Modified: py/trunk/py/doc/test.txt Log: add an overview over py.tests command-line options Modified: py/trunk/py/doc/test.txt ============================================================================== --- py/trunk/py/doc/test.txt (original) +++ py/trunk/py/doc/test.txt Tue Feb 6 00:50:11 2007 @@ -370,6 +370,130 @@ your setup function callable. Did we mention that lazyness is a virtue? +Some ``py.test`` command-line options +===================================== + +Regular options +--------------- + +``-v, --verbose``: + + Increase verbosity. This shows a test per line while running and also + shows the traceback after interrupting the test run with Ctrl-C. + + +``-x, --exitfirst``: + + exit instantly on the first error or the first failed test. + + +``-s, --nocapture``: + + disable catching of sys.stdout/stderr output. + + +``-k KEYWORD``: + + only run test items matching the given keyword expression. You can also add + use ``-k -KEYWORD`` to exlude tests from being run. The keyword is matched + against filename, test class name, method name. + + +``-l, --showlocals``: + show locals in tracebacks: for every frame in the traceback, show the values + of the local variables. + + +``--pdb``: + + drop into pdb (the `Python debugger`_) on exceptions. If the debugger is + quitted, the next test is run. This implies ``-s``. + + +``--tb=TBSTYLE``: + + traceback verboseness: ``long`` is the default, ``short`` are the normal + Python tracebacks, ``no`` omits tracebacks completely. + + +``--fulltrace``: + + Don't cut any tracebacks. The default is to leave out frames if an infinite + recursion is detected. + + +``--nomagic``: + + Refrain from using magic as much as possible. This can be useful if you are + suspicious that ``py.test`` somehow interferes with your program in + unintended ways (if this is the case, please contact us!). + + +``--collectonly``: + + Only collect tests, don't execute them. + + +``--traceconfig``: + + trace considerations of conftest.py files. Useful when you have various + conftest.py files around and are unsure about their interaction. + + +More experimental options +------------------------- + +**Note**: these options could change in the future. + + +``-f, --looponfailing``: + + Loop on failing test set. This is a feature you can use when you are trying + to fix a number of failing tests: First all the tests are being run. If a + number of tests are failing, these are run repeatedly afterwards. Every + repetition is started once a file below the directory that you started + testing for is changed. If one of the previously failing tests now passes, + it is removed from the test set. + +``--exec=EXECUTABLE``: + + Python executable to run the tests with. Useful for testing on different + versions of Python. + + +``-d, --dist``: + + ad-hoc `distribute tests across machines`_ (requires conftest settings) + + +``-w, --startserver``: + + starts local web server for displaying test progress. + + +``-r, --runbrowser``: + + Run browser (implies --startserver). + + +``--box``: + + Use boxing: run each test in an external process. Very useful for testing + things that occasionally segfault (since normally the segfault then would + stop the whole test process). + +``--rest``: + + `reStructured Text`_ output reporting. + + +.. _`reStructured Text`: http://docutils.sourceforge.net +.. _`Python debugger`: http://docs.python.org/lib/module-pdb.html + + +.. _`distribute tests across machines`: + + Automated Distributed Testing ================================== From fijal at codespeak.net Tue Feb 6 00:53:39 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 6 Feb 2007 00:53:39 +0100 (CET) Subject: [py-svn] r37990 - in py/trunk/py/test/rsession: . testing Message-ID: <20070205235339.67F2C10077@code0.codespeak.net> Author: fijal Date: Tue Feb 6 00:53:29 2007 New Revision: 37990 Modified: py/trunk/py/test/rsession/hostmanage.py py/trunk/py/test/rsession/testing/test_hostmanage.py Log: Fix reporting (and a test for that) Modified: py/trunk/py/test/rsession/hostmanage.py ============================================================================== --- py/trunk/py/test/rsession/hostmanage.py (original) +++ py/trunk/py/test/rsession/hostmanage.py Tue Feb 6 00:53:29 2007 @@ -122,23 +122,31 @@ host.initgateway(python=dist_remotepython) host.gw.host = host + def _send_rsync(self, root, reporter, ignores, make_callback=False): + rsync = HostRSync(ignores=ignores) + destrelpath = root.relto(self.config.topdir) + for host in self.hosts: + reporter(repevent.HostRSyncing(host)) + if make_callback: + def donecallback(): + reporter(repevent.HostReady(host)) + else: + donecallback = None + rsync.add_target_host(host, destrelpath, + finishedcallback=donecallback) + rsync.send(root) + + def init_rsync(self, reporter): - # send each rsync root + # send each rsync root roots = self.config.getvalue_pathlist("dist_rsync_roots") ignores = self.config.getvalue_pathlist("dist_rsync_ignore") if roots is None: roots = [self.config.topdir] self.prepare_gateways() - rsync = HostRSync(ignores=ignores) - for root in roots: - destrelpath = root.relto(self.config.topdir) - for host in self.hosts: - reporter(repevent.HostRSyncing(host)) - def donecallback(): - reporter(repevent.HostReady(host)) - rsync.add_target_host(host, destrelpath, - finishedcallback=donecallback) - rsync.send(root) + for root in roots[:-1]: + self._send_rsync(root, reporter, ignores) + self._send_rsync(roots[-1], reporter, ignores, True) def setup_hosts(self, reporter): self.init_rsync(reporter) Modified: py/trunk/py/test/rsession/testing/test_hostmanage.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_hostmanage.py (original) +++ py/trunk/py/test/rsession/testing/test_hostmanage.py Tue Feb 6 00:53:29 2007 @@ -4,7 +4,8 @@ import py from py.__.test.rsession.hostmanage import HostRSync -from py.__.test.rsession.hostmanage import HostInfo, HostManager +from py.__.test.rsession.hostmanage import HostInfo, HostManager +from py.__.test.rsession import repevent class DirSetup: def setup_method(self, method): @@ -177,3 +178,15 @@ assert self.dest.join("dir5","file").check() assert not self.dest.join("dir6").check() + def test_hostmanager_rsync_reported_once(self): + dir2 = self.source.ensure("dir1", "dir2", dir=1) + dir5 = self.source.ensure("dir5", "dir6", "bogus") + dirf = self.source.ensure("dir3", "file") + config = py.test.config._reparse([self.source]) + hm = HostManager(config, + hosts=[HostInfo("localhost:" + str(self.dest)) + for i in range(3)]) + events = [] + hm.init_rsync(reporter=events.append) + readies = [i for i in events if isinstance(i, repevent.HostReady)] + assert len(readies) == 3 From cfbolz at codespeak.net Tue Feb 6 00:53:48 2007 From: cfbolz at codespeak.net (cfbolz at codespeak.net) Date: Tue, 6 Feb 2007 00:53:48 +0100 (CET) Subject: [py-svn] r37991 - py/trunk/py/doc Message-ID: <20070205235348.D6AB81007F@code0.codespeak.net> Author: cfbolz Date: Tue Feb 6 00:53:46 2007 New Revision: 37991 Modified: py/trunk/py/doc/test.txt Log: remove colons and change the subsecion title Modified: py/trunk/py/doc/test.txt ============================================================================== --- py/trunk/py/doc/test.txt (original) +++ py/trunk/py/doc/test.txt Tue Feb 6 00:53:46 2007 @@ -376,77 +376,77 @@ Regular options --------------- -``-v, --verbose``: +``-v, --verbose`` Increase verbosity. This shows a test per line while running and also shows the traceback after interrupting the test run with Ctrl-C. -``-x, --exitfirst``: +``-x, --exitfirst`` exit instantly on the first error or the first failed test. -``-s, --nocapture``: +``-s, --nocapture`` disable catching of sys.stdout/stderr output. -``-k KEYWORD``: +``-k KEYWORD`` only run test items matching the given keyword expression. You can also add use ``-k -KEYWORD`` to exlude tests from being run. The keyword is matched against filename, test class name, method name. -``-l, --showlocals``: +``-l, --showlocals`` show locals in tracebacks: for every frame in the traceback, show the values of the local variables. -``--pdb``: +``--pdb`` drop into pdb (the `Python debugger`_) on exceptions. If the debugger is quitted, the next test is run. This implies ``-s``. -``--tb=TBSTYLE``: +``--tb=TBSTYLE`` traceback verboseness: ``long`` is the default, ``short`` are the normal Python tracebacks, ``no`` omits tracebacks completely. -``--fulltrace``: +``--fulltrace`` Don't cut any tracebacks. The default is to leave out frames if an infinite recursion is detected. -``--nomagic``: +``--nomagic`` Refrain from using magic as much as possible. This can be useful if you are suspicious that ``py.test`` somehow interferes with your program in unintended ways (if this is the case, please contact us!). -``--collectonly``: +``--collectonly`` Only collect tests, don't execute them. -``--traceconfig``: +``--traceconfig`` trace considerations of conftest.py files. Useful when you have various conftest.py files around and are unsure about their interaction. -More experimental options -------------------------- +experimental options +-------------------- **Note**: these options could change in the future. -``-f, --looponfailing``: +``-f, --looponfailing`` Loop on failing test set. This is a feature you can use when you are trying to fix a number of failing tests: First all the tests are being run. If a @@ -455,34 +455,34 @@ testing for is changed. If one of the previously failing tests now passes, it is removed from the test set. -``--exec=EXECUTABLE``: +``--exec=EXECUTABLE`` Python executable to run the tests with. Useful for testing on different versions of Python. -``-d, --dist``: +``-d, --dist`` ad-hoc `distribute tests across machines`_ (requires conftest settings) -``-w, --startserver``: +``-w, --startserver`` starts local web server for displaying test progress. -``-r, --runbrowser``: +``-r, --runbrowser`` Run browser (implies --startserver). -``--box``: +``--box`` Use boxing: run each test in an external process. Very useful for testing things that occasionally segfault (since normally the segfault then would stop the whole test process). -``--rest``: +``--rest`` `reStructured Text`_ output reporting. From cfbolz at codespeak.net Tue Feb 6 00:56:19 2007 From: cfbolz at codespeak.net (cfbolz at codespeak.net) Date: Tue, 6 Feb 2007 00:56:19 +0100 (CET) Subject: [py-svn] r37992 - py/trunk/py/doc Message-ID: <20070205235619.59DE510077@code0.codespeak.net> Author: cfbolz Date: Tue Feb 6 00:56:14 2007 New Revision: 37992 Modified: py/trunk/py/doc/test.txt Log: have real definitions (yes, the empty lines make a difference). rest is strange. Modified: py/trunk/py/doc/test.txt ============================================================================== --- py/trunk/py/doc/test.txt (original) +++ py/trunk/py/doc/test.txt Tue Feb 6 00:56:14 2007 @@ -377,23 +377,19 @@ --------------- ``-v, --verbose`` - Increase verbosity. This shows a test per line while running and also shows the traceback after interrupting the test run with Ctrl-C. ``-x, --exitfirst`` - exit instantly on the first error or the first failed test. ``-s, --nocapture`` - disable catching of sys.stdout/stderr output. ``-k KEYWORD`` - only run test items matching the given keyword expression. You can also add use ``-k -KEYWORD`` to exlude tests from being run. The keyword is matched against filename, test class name, method name. @@ -405,37 +401,31 @@ ``--pdb`` - drop into pdb (the `Python debugger`_) on exceptions. If the debugger is quitted, the next test is run. This implies ``-s``. ``--tb=TBSTYLE`` - traceback verboseness: ``long`` is the default, ``short`` are the normal Python tracebacks, ``no`` omits tracebacks completely. ``--fulltrace`` - Don't cut any tracebacks. The default is to leave out frames if an infinite recursion is detected. ``--nomagic`` - Refrain from using magic as much as possible. This can be useful if you are suspicious that ``py.test`` somehow interferes with your program in unintended ways (if this is the case, please contact us!). ``--collectonly`` - Only collect tests, don't execute them. ``--traceconfig`` - trace considerations of conftest.py files. Useful when you have various conftest.py files around and are unsure about their interaction. @@ -447,7 +437,6 @@ ``-f, --looponfailing`` - Loop on failing test set. This is a feature you can use when you are trying to fix a number of failing tests: First all the tests are being run. If a number of tests are failing, these are run repeatedly afterwards. Every @@ -456,34 +445,28 @@ it is removed from the test set. ``--exec=EXECUTABLE`` - Python executable to run the tests with. Useful for testing on different versions of Python. ``-d, --dist`` - ad-hoc `distribute tests across machines`_ (requires conftest settings) ``-w, --startserver`` - starts local web server for displaying test progress. ``-r, --runbrowser`` - Run browser (implies --startserver). ``--box`` - Use boxing: run each test in an external process. Very useful for testing things that occasionally segfault (since normally the segfault then would stop the whole test process). ``--rest`` - `reStructured Text`_ output reporting. From fijal at codespeak.net Tue Feb 6 11:31:14 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 6 Feb 2007 11:31:14 +0100 (CET) Subject: [py-svn] r37994 - in py/trunk/py/test/rsession: . testing webdata Message-ID: <20070206103114.5A55A1007C@code0.codespeak.net> Author: fijal Date: Tue Feb 6 11:31:08 2007 New Revision: 37994 Modified: py/trunk/py/test/rsession/hostmanage.py py/trunk/py/test/rsession/repevent.py py/trunk/py/test/rsession/reporter.py py/trunk/py/test/rsession/rest.py py/trunk/py/test/rsession/rsession.py py/trunk/py/test/rsession/testing/test_hostmanage.py py/trunk/py/test/rsession/testing/test_master.py py/trunk/py/test/rsession/testing/test_reporter.py py/trunk/py/test/rsession/testing/test_rest.py py/trunk/py/test/rsession/testing/test_rsession.py py/trunk/py/test/rsession/testing/test_web.py py/trunk/py/test/rsession/testing/test_webjs.py py/trunk/py/test/rsession/webdata/source.js Log: Imrpovement of host rsync reporting. Additionally it increased stability for no particular reason Modified: py/trunk/py/test/rsession/hostmanage.py ============================================================================== --- py/trunk/py/test/rsession/hostmanage.py (original) +++ py/trunk/py/test/rsession/hostmanage.py Tue Feb 6 11:31:08 2007 @@ -122,21 +122,6 @@ host.initgateway(python=dist_remotepython) host.gw.host = host - def _send_rsync(self, root, reporter, ignores, make_callback=False): - rsync = HostRSync(ignores=ignores) - destrelpath = root.relto(self.config.topdir) - for host in self.hosts: - reporter(repevent.HostRSyncing(host)) - if make_callback: - def donecallback(): - reporter(repevent.HostReady(host)) - else: - donecallback = None - rsync.add_target_host(host, destrelpath, - finishedcallback=donecallback) - rsync.send(root) - - def init_rsync(self, reporter): # send each rsync root roots = self.config.getvalue_pathlist("dist_rsync_roots") @@ -144,9 +129,18 @@ if roots is None: roots = [self.config.topdir] self.prepare_gateways() - for root in roots[:-1]: - self._send_rsync(root, reporter, ignores) - self._send_rsync(roots[-1], reporter, ignores, True) + for host in self.hosts: + reporter(repevent.HostRSyncRoots(host, roots)) + for root in roots: + rsync = HostRSync(ignores=ignores) + destrelpath = root.relto(self.config.topdir) + for host in self.hosts: + reporter(repevent.HostRSyncing(host)) + def donecallback(): + reporter(repevent.HostRSyncRootReady(host, root)) + rsync.add_target_host(host, destrelpath, + finishedcallback=donecallback) + rsync.send(root) def setup_hosts(self, reporter): self.init_rsync(reporter) Modified: py/trunk/py/test/rsession/repevent.py ============================================================================== --- py/trunk/py/test/rsession/repevent.py (original) +++ py/trunk/py/test/rsession/repevent.py Tue Feb 6 11:31:08 2007 @@ -77,9 +77,15 @@ def __init__(self, host): self.host = host -class HostReady(ReportEvent): - def __init__(self, host): +class HostRSyncRoots(ReportEvent): + def __init__(self, host, roots): + self.host = host + self.roots = roots + +class HostRSyncRootReady(ReportEvent): + def __init__(self, host, root): self.host = host + self.root = root class TestStarted(ReportEvent): def __init__(self, hosts): Modified: py/trunk/py/test/rsession/reporter.py ============================================================================== --- py/trunk/py/test/rsession/reporter.py (original) +++ py/trunk/py/test/rsession/reporter.py Tue Feb 6 11:31:08 2007 @@ -26,6 +26,7 @@ self.failed = dict([(host, 0) for host in hosts]) self.skipped = dict([(host, 0) for host in hosts]) self.passed = dict([(host, 0) for host in hosts]) + self.to_rsync = {} def get_item_name(self, event, colitem): return "/".join(colitem.listnames()) @@ -58,8 +59,16 @@ def report_HostRSyncing(self, item): print "%10s: RSYNC ==> %s" % (item.host.hostname[:10], item.host.relpath) + + def report_HostRSyncRoots(self, item): + self.to_rsync[item.host] = len(item.roots) + + def report_HostRSyncRootReady(self, item): + self.to_rsync[item.host] -= 1 + if not self.to_rsync[item.host]: + self._host_ready(item) - def report_HostReady(self, item): + def _host_ready(self, item): self.hosts_to_rsync -= 1 if self.hosts_to_rsync: print "%10s: READY (still %d to go)" % (item.host.hostname[:10], Modified: py/trunk/py/test/rsession/rest.py ============================================================================== --- py/trunk/py/test/rsession/rest.py (original) +++ py/trunk/py/test/rsession/rest.py Tue Feb 6 11:31:08 2007 @@ -46,7 +46,7 @@ self.add_rest(LiteralBlock('%10s: RSYNC ==> %s' % (item.host.hostname[:10], item.host.relpath))) - def report_HostReady(self, item): + def _host_ready(self, item): self.add_rest(LiteralBlock('%10s: READY' % (item.host.hostname[:10],))) def report_TestStarted(self, event): Modified: py/trunk/py/test/rsession/rsession.py ============================================================================== --- py/trunk/py/test/rsession/rsession.py (original) +++ py/trunk/py/test/rsession/rsession.py Tue Feb 6 11:31:08 2007 @@ -17,6 +17,8 @@ from py.__.test.session import Session from py.__.test.outcome import Skipped, Failed +old_fork = os.fork + class AbstractSession(Session): """ An abstract session executes collectors/items through a runner. @@ -86,7 +88,7 @@ def wrap_reporter(self, reporter): """ We wrap reporter around, which makes it possible to us to track - number of failures + existance of failures """ self.was_failure = False def new_reporter(event): Modified: py/trunk/py/test/rsession/testing/test_hostmanage.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_hostmanage.py (original) +++ py/trunk/py/test/rsession/testing/test_hostmanage.py Tue Feb 6 11:31:08 2007 @@ -179,6 +179,7 @@ assert not self.dest.join("dir6").check() def test_hostmanager_rsync_reported_once(self): + py.test.skip("XXX not needed any more") dir2 = self.source.ensure("dir1", "dir2", dir=1) dir5 = self.source.ensure("dir5", "dir6", "bogus") dirf = self.source.ensure("dir3", "file") Modified: py/trunk/py/test/rsession/testing/test_master.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_master.py (original) +++ py/trunk/py/test/rsession/testing/test_master.py Tue Feb 6 11:31:08 2007 @@ -176,7 +176,7 @@ gw.host = HostInfo("localhost") gw.host.gw = gw config = py.test.config._reparse([tmpdir]) - channel = setup_slave(gw, pkgdir, config) + channel = setup_slave(gw.host, config) mn = MasterNode(channel, reports.append, {}) return mn, gw, channel Modified: py/trunk/py/test/rsession/testing/test_reporter.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_reporter.py (original) +++ py/trunk/py/test/rsession/testing/test_reporter.py Tue Feb 6 11:31:08 2007 @@ -161,9 +161,11 @@ hosts = [HostInfo(i) for i in ["host1", "host2", "host3"]] r = self.reporter(config, hosts) r.report(repevent.TestStarted(hosts)) - r.report(repevent.HostReady(hosts[0])) - r.report(repevent.HostReady(hosts[1])) - r.report(repevent.HostReady(hosts[2])) + for host in hosts: + r.report(repevent.HostRSyncRoots(host, ["a", "b", "c"])) + for host in hosts: + for root in ["a", "b", "c"]: + r.report(repevent.HostRSyncRootReady(host, root)) out, err = cap.reset() assert not err expected1 = "Test started, hosts: host1, host2, host3" Modified: py/trunk/py/test/rsession/testing/test_rest.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_rest.py (original) +++ py/trunk/py/test/rsession/testing/test_rest.py Tue Feb 6 11:31:08 2007 @@ -57,8 +57,11 @@ assert stdout.getvalue() == ('::\n\n localhost: RSYNC ==> ' '/foo/bar\n\n') - def test_report_HostReady(self): - event = repevent.HostReady(HostInfo('localhost')) + def test_report_HostRSyncRootReady(self): + h = HostInfo('localhost') + reporter.hosts_to_rsync = 1 + reporter.report(repevent.HostRSyncRoots(h, ["a"])) + event = repevent.HostRSyncRootReady(h, "a") reporter.report(event) assert stdout.getvalue() == '::\n\n localhost: READY\n\n' Modified: py/trunk/py/test/rsession/testing/test_rsession.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_rsession.py (original) +++ py/trunk/py/test/rsession/testing/test_rsession.py Tue Feb 6 11:31:08 2007 @@ -80,9 +80,9 @@ pass def test_5(): assert __file__ != '%s' - def test_6(): - import py - assert py.__file__ != '%s' + #def test_6(): + # import py + # assert py.__file__ != '%s' """ % (tmpdir.join(subdir), py.__file__))) destdir.join("py").mksymlinkto(py.path.local(py.__file__).dirpath()) config = py.test.config._reparse([tmpdir.join(subdir)]) @@ -98,8 +98,8 @@ passevents = [i for i in testevents if i.outcome.passed] failevents = [i for i in testevents if i.outcome.excinfo] skippedevents = [i for i in testevents if i.outcome.skipped] - assert len(testevents) == 6 - assert len(passevents) == 3 + assert len(testevents) == 5 + assert len(passevents) == 2 assert len(failevents) == 3 tb = failevents[0].outcome.excinfo.traceback assert str(tb[0].path).find("test_one") != -1 @@ -130,7 +130,7 @@ if isinstance(i, repevent.HostRSyncing)] assert len(count_rsyn_calls) == len([i for i in hosts]) count_ready_calls = [i for i in setup_events - if isinstance(i, repevent.HostReady)] + if isinstance(i, repevent.HostRSyncRootReady)] assert len(count_ready_calls) == len([i for i in hosts]) # same for teardown events Modified: py/trunk/py/test/rsession/testing/test_web.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_web.py (original) +++ py/trunk/py/test/rsession/testing/test_web.py Tue Feb 6 11:31:08 2007 @@ -3,6 +3,7 @@ """ import py +py.test.skip("WIP") try: from pypy.translator.js.main import rpython2javascript Modified: py/trunk/py/test/rsession/testing/test_webjs.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_webjs.py (original) +++ py/trunk/py/test/rsession/testing/test_webjs.py Tue Feb 6 11:31:08 2007 @@ -1,5 +1,7 @@ import py +py.test.skip("WIP") + try: import pypy from pypy.translator.js.modules import dom Modified: py/trunk/py/test/rsession/webdata/source.js ============================================================================== Binary files. No diff available. From fijal at codespeak.net Tue Feb 6 12:15:09 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 6 Feb 2007 12:15:09 +0100 (CET) Subject: [py-svn] r38000 - in py/trunk/py/test/rsession: . testing webdata Message-ID: <20070206111509.732131007D@code0.codespeak.net> Author: fijal Date: Tue Feb 6 12:15:06 2007 New Revision: 38000 Modified: py/trunk/py/test/rsession/testing/test_web.py py/trunk/py/test/rsession/testing/test_webjs.py py/trunk/py/test/rsession/web.py py/trunk/py/test/rsession/webdata/source.js Log: Fix web reporter Modified: py/trunk/py/test/rsession/testing/test_web.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_web.py (original) +++ py/trunk/py/test/rsession/testing/test_web.py Tue Feb 6 12:15:06 2007 @@ -3,7 +3,6 @@ """ import py -py.test.skip("WIP") try: from pypy.translator.js.main import rpython2javascript Modified: py/trunk/py/test/rsession/testing/test_webjs.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_webjs.py (original) +++ py/trunk/py/test/rsession/testing/test_webjs.py Tue Feb 6 12:15:06 2007 @@ -1,7 +1,5 @@ import py -py.test.skip("WIP") - try: import pypy from pypy.translator.js.modules import dom Modified: py/trunk/py/test/rsession/web.py ============================================================================== --- py/trunk/py/test/rsession/web.py (original) +++ py/trunk/py/test/rsession/web.py Tue Feb 6 12:15:06 2007 @@ -256,7 +256,7 @@ elif isinstance(event, repevent.SendItem): args = add_item(event) args['hostkey'] = event.channel.gateway.host.hostid - elif isinstance(event, repevent.HostReady): + elif isinstance(event, repevent.HostRSyncRootReady): self.ready_hosts[event.host] = True args = {'hostname' : event.host.hostname, 'hostkey' : event.host.hostid} elif isinstance(event, repevent.FailedTryiter): @@ -299,6 +299,18 @@ def report_unknown(self, event): # XXX: right now, we just pass it for showing self.pending_events.put(event) + + def _host_ready(self, event): + self.pending_events.put(event) + + def report_HostRSyncRoots(self, item): + self.to_rsync[item.host] = len(item.roots) + + def report_HostRSyncRootReady(self, item): + self.to_rsync[item.host] -= 1 + if not self.to_rsync[item.host]: + self._host_ready(item) + def report_TestStarted(self, event): # XXX: It overrides out self.hosts Modified: py/trunk/py/test/rsession/webdata/source.js ============================================================================== Binary files. No diff available. From guido at codespeak.net Tue Feb 6 14:19:19 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Tue, 6 Feb 2007 14:19:19 +0100 (CET) Subject: [py-svn] r38005 - in py/trunk/py/apigen: . testing Message-ID: <20070206131919.3C3861006E@code0.codespeak.net> Author: guido Date: Tue Feb 6 14:19:16 2007 New Revision: 38005 Modified: py/trunk/py/apigen/apigen.py py/trunk/py/apigen/html.py py/trunk/py/apigen/htmlgen.py py/trunk/py/apigen/style.css py/trunk/py/apigen/testing/test_apigen_functional.py py/trunk/py/apigen/testing/test_htmlgen.py Log: Made that source snippets are now shown in two tables, to allow selecting the source without the line numbers, decreased font size a bit, made that the Page class can be passed in from the outside (build() method) to more easily allow customization of the layout and pages. Modified: py/trunk/py/apigen/apigen.py ============================================================================== --- py/trunk/py/apigen/apigen.py (original) +++ py/trunk/py/apigen/apigen.py Tue Feb 6 14:19:16 2007 @@ -12,6 +12,8 @@ from py.__.apigen import project from py.__.apigen.tracer.docstorage import pkg_to_dict +from layout import LayoutPage + def get_documentable_items_pkgdir(pkgdir): """ get all documentable items from an initpkg pkgdir @@ -31,20 +33,27 @@ return pkgname, pkgdict def build(pkgdir, dsa, capture): + # create a linker (link database) for cross-linking l = linker.Linker() + + # create a project.Project instance to contain the LayoutPage instances proj = project.Project() + # output dir if 'APIGEN_TARGET' in os.environ: targetdir = py.path.local(os.environ['APIGEN_TARGET']) else: targetdir = pkgdir.dirpath().join('apigen') targetdir.ensure(dir=True) + # find out what to build all_names = dsa._get_names(filter=lambda x, y: True) namespace_tree = htmlgen.create_namespace_tree(all_names) + + # and build it apb = htmlgen.ApiPageBuilder(targetdir, l, dsa, pkgdir, namespace_tree, - capture) - spb = htmlgen.SourcePageBuilder(targetdir, l, pkgdir, capture) + capture, LayoutPage) + spb = htmlgen.SourcePageBuilder(targetdir, l, pkgdir, capture, LayoutPage) capture.err.writeorg('preparing namespace pages\n') ns_data = apb.prepare_namespace_pages() Modified: py/trunk/py/apigen/html.py ============================================================================== --- py/trunk/py/apigen/html.py (original) +++ py/trunk/py/apigen/html.py Tue Feb 6 14:19:16 2007 @@ -4,7 +4,8 @@ # HTML related stuff class H(html): class Content(html.div): - pass # style = html.Style(margin_left='15em') + def __init__(self, *args): + super(H.Content, self).__init__(id='apigen-content', *args) class Description(html.div): pass @@ -88,12 +89,29 @@ if href: link = H.a(text, href=href) super(H.SourceSnippet, self).__init__( - link, H.div(class_='code', *sourceels)) + link, H.div(*sourceels)) class SourceDef(html.div): def __init__(self, *sourceels): super(H.SourceDef, self).__init__( - H.div(class_='code', *sourceels)) + H.div(*sourceels)) + + class SourceCode(html.div): + style = html.Style(margin_top='1em', margin_bottom='1em') + def __init__(self): + self.linenotable = lntable = H.table(style='float: left') + self.linenotbody = lntbody = H.tbody() + lntable.append(lntbody) + + self.linetable = ltable = H.table() + self.linetbody = ltbody = H.tbody() + ltable.append(ltbody) + + super(H.SourceCode, self).__init__(lntable, ltable) + + def add_line(self, lineno, els): + self.linenotbody.append(H.tr(H.td(lineno, class_='lineno'))) + self.linetbody.append(H.tr(H.td(class_='code', *els))) class NonPythonSource(html.pre): pass # style = html.Style(margin_left='15em') Modified: py/trunk/py/apigen/htmlgen.py ============================================================================== --- py/trunk/py/apigen/htmlgen.py (original) +++ py/trunk/py/apigen/htmlgen.py Tue Feb 6 14:19:16 2007 @@ -115,32 +115,65 @@ ret[ns].append(itempath) return ret -def wrap_page(project, title, contentel, navel, relbase, basepath): - page = LayoutPage(project, title, nav=navel, encoding='UTF-8', +def wrap_page(project, title, contentel, navel, relbase, basepath, + pageclass): + page = pageclass(project, title, nav=navel, encoding='UTF-8', relpath=relbase) page.set_content(contentel) page.setup_scripts_styles(basepath) return page +def enumerate_and_color(codelines, firstlineno, enc): + snippet = H.SourceCode() + tokenizer = source_color.Tokenizer(source_color.PythonSchema) + for i, line in enumerate(codelines): + try: + snippet.add_line(i + firstlineno + 1, + source_html.prepare_line([line], tokenizer, enc)) + except py.error.ENOENT: + # error reading source code, giving up + snippet = org + break + return snippet + +def get_obj(pkg, dotted_name): + full_dotted_name = '%s.%s' % (pkg.__name__, dotted_name) + if dotted_name == '': + return pkg + path = dotted_name.split('.') + ret = pkg + for item in path: + marker = [] + ret = getattr(ret, item, marker) + if ret is marker: + raise NameError('can not access %s in %s' % (item, + full_dotted_name)) + return ret + # the PageBuilder classes take care of producing the docs (using the stuff # above) class AbstractPageBuilder(object): + pageclass = LayoutPage + def write_page(self, title, reltargetpath, project, tag, nav): targetpath = self.base.join(reltargetpath) relbase= relpath('%s%s' % (targetpath.dirpath(), targetpath.sep), self.base.strpath + '/') - page = wrap_page(project, title, tag, nav, relbase, self.base) + page = wrap_page(project, title, tag, nav, relbase, self.base, + self.pageclass) content = self.linker.call_withbase(reltargetpath, page.unicode) targetpath.ensure() targetpath.write(content.encode("utf8")) class SourcePageBuilder(AbstractPageBuilder): """ builds the html for a source docs page """ - def __init__(self, base, linker, projroot, capture=None): + def __init__(self, base, linker, projroot, capture=None, + pageclass=LayoutPage): self.base = base self.linker = linker self.projroot = projroot self.capture = capture + self.pageclass = pageclass def build_navigation(self, fspath): nav = H.Navigation(class_='sidebar') @@ -191,7 +224,7 @@ source = fspath.read() sep = get_linesep(source) colored = enumerate_and_color(source.split(sep), 0, enc) - tag = H.SourceDef(*colored) + tag = H.SourceDef(colored) nav = self.build_navigation(fspath) return tag, nav @@ -260,38 +293,10 @@ '/') self.write_page(title, reltargetpath, project, tag, nav) -def enumerate_and_color(codelines, firstlineno, enc): - tokenizer = source_color.Tokenizer(source_color.PythonSchema) - colored = [] - for i, line in enumerate(codelines): - try: - colored.append(H.span('%04s: ' % (i + firstlineno + 1))) - colored.append(source_html.prepare_line([line], tokenizer, enc)) - colored.append('\n') - except py.error.ENOENT: - # error reading source code, giving up - colored = org - break - return colored - -def get_obj(pkg, dotted_name): - full_dotted_name = '%s.%s' % (pkg.__name__, dotted_name) - if dotted_name == '': - return pkg - path = dotted_name.split('.') - ret = pkg - for item in path: - marker = [] - ret = getattr(ret, item, marker) - if ret is marker: - raise NameError('can not access %s in %s' % (item, - full_dotted_name)) - return ret - class ApiPageBuilder(AbstractPageBuilder): """ builds the html for an api docs page """ def __init__(self, base, linker, dsa, projroot, namespace_tree, - capture=None): + capture=None, pageclass=LayoutPage): self.base = base self.linker = linker self.dsa = dsa @@ -299,6 +304,7 @@ self.projpath = py.path.local(projroot) self.namespace_tree = namespace_tree self.capture = capture + self.pageclass = pageclass pkgname = self.dsa.get_module_name().split('/')[-1] self.pkg = __import__(pkgname) @@ -327,7 +333,7 @@ firstlineno = func.func_code.co_firstlineno sep = get_linesep(callable_source) org = callable_source.split(sep) - colored = enumerate_and_color(org, firstlineno, enc) + colored = [enumerate_and_color(org, firstlineno, enc)] text = 'source: %s' % (sourcefile,) if is_in_pkg: href = self.linker.get_lazyhref(sourcefile) @@ -657,11 +663,12 @@ else: enc = 'latin-1' sourcelink = H.div(linktext) - colored = enumerate_and_color(mangled, frame.firstlineno, enc) + colored = [enumerate_and_color(mangled, + frame.firstlineno, enc)] else: sourcelink = H.div('source unknown (%s)' % (sourcefile,)) colored = mangled[:] tbdiv.append(sourcelink) - tbdiv.append(H.div(class_='code', *colored)) + tbdiv.append(H.div(*colored)) return tbdiv Modified: py/trunk/py/apigen/style.css ============================================================================== --- py/trunk/py/apigen/style.css (original) +++ py/trunk/py/apigen/style.css Tue Feb 6 14:19:16 2007 @@ -1,5 +1,10 @@ +#apigen-content { + font-size: 0.8em; +} + div.sidebar { font-family: Verdana, Helvetica, Arial, sans-serif; + font-size: 0.9em; width: 155px; vertical-align: top; margin-top: 0.5em; Modified: py/trunk/py/apigen/testing/test_apigen_functional.py ============================================================================== --- py/trunk/py/apigen/testing/test_apigen_functional.py (original) +++ py/trunk/py/apigen/testing/test_apigen_functional.py Tue Feb 6 14:19:16 2007 @@ -26,6 +26,14 @@ def get_somevar(self): " get_somevar docstring " return self.somevar + + def get_some_source(self): + ret = py.code.Source('''\\ + def foo(): + return 'bar' + ''') + return ret + """)) temp.ensure('pak/sometestsubclass.py').write(py.code.Source("""\ from sometestclass import SomeTestClass Modified: py/trunk/py/apigen/testing/test_htmlgen.py ============================================================================== --- py/trunk/py/apigen/testing/test_htmlgen.py (original) +++ py/trunk/py/apigen/testing/test_htmlgen.py Tue Feb 6 14:19:16 2007 @@ -47,13 +47,54 @@ colored = htmlgen.enumerate_and_color(['def foo():', ' print "bar"'], 0, 'ascii') div = py.xml.html.div(*colored).unicode(indent=0) - assert div == ('
' - ' 1: ' - 'def foo():\n' - ' 2: ' - ' print' - ' "bar"\n' - '
') + print repr(div) + assert div == (u'
' + '' + '' + '' + '' + '' + '
1
2
' + '' + '' + '' + '' + '' + '
' + 'def foo():' + '
' + ' print' + ' "bar"' + '
' + '
') + +def test_enumerate_and_color_multiline(): + colored = htmlgen.enumerate_and_color(['code = """\\', 'foo bar', '"""'], + 0, 'ascii') + div = py.xml.html.div(*colored).unicode(indent=0) + print repr(div) + assert div == (u'
' + '' + '' + '' + '' + '' + '' + '
1
2
3
' + '' + '' + '' + '' + '' + '' + '
' + 'code = """\\' + '
' + 'foo bar' + '
' + '"""' + '
' + '
') def test_show_property(): assert htmlgen.show_property('foo') From fijal at codespeak.net Tue Feb 6 19:44:19 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 6 Feb 2007 19:44:19 +0100 (CET) Subject: [py-svn] r38021 - in py/trunk/py/test/rsession: . testing Message-ID: <20070206184419.63D4410063@code0.codespeak.net> Author: fijal Date: Tue Feb 6 19:44:16 2007 New Revision: 38021 Modified: py/trunk/py/test/rsession/hostmanage.py py/trunk/py/test/rsession/repevent.py py/trunk/py/test/rsession/reporter.py py/trunk/py/test/rsession/rsession.py py/trunk/py/test/rsession/testing/test_reporter.py py/trunk/py/test/rsession/testing/test_rest.py Log: Simple refactoring to be a bit more verbose when starting. Modified: py/trunk/py/test/rsession/hostmanage.py ============================================================================== --- py/trunk/py/test/rsession/hostmanage.py (original) +++ py/trunk/py/test/rsession/hostmanage.py Tue Feb 6 19:44:16 2007 @@ -115,6 +115,10 @@ hosts = self.config.getvalue("dist_hosts") hosts = [HostInfo(x) for x in hosts] self.hosts = hosts + roots = self.config.getvalue_pathlist("dist_rsync_roots") + if roots is None: + roots = [self.config.topdir] + self.roots = roots def prepare_gateways(self): dist_remotepython = self.config.getvalue("dist_remotepython") @@ -124,14 +128,11 @@ def init_rsync(self, reporter): # send each rsync root - roots = self.config.getvalue_pathlist("dist_rsync_roots") ignores = self.config.getvalue_pathlist("dist_rsync_ignore") - if roots is None: - roots = [self.config.topdir] self.prepare_gateways() for host in self.hosts: - reporter(repevent.HostRSyncRoots(host, roots)) - for root in roots: + reporter(repevent.HostRSyncRoots(host, self.roots)) + for root in self.roots: rsync = HostRSync(ignores=ignores) destrelpath = root.relto(self.config.topdir) for host in self.hosts: Modified: py/trunk/py/test/rsession/repevent.py ============================================================================== --- py/trunk/py/test/rsession/repevent.py (original) +++ py/trunk/py/test/rsession/repevent.py Tue Feb 6 19:44:16 2007 @@ -88,8 +88,10 @@ self.root = root class TestStarted(ReportEvent): - def __init__(self, hosts): + def __init__(self, hosts, topdir, roots): self.hosts = hosts + self.topdir = topdir + self.roots = roots self.timestart = time.time() class TestFinished(ReportEvent): Modified: py/trunk/py/test/rsession/reporter.py ============================================================================== --- py/trunk/py/test/rsession/reporter.py (original) +++ py/trunk/py/test/rsession/reporter.py Tue Feb 6 19:44:16 2007 @@ -82,6 +82,11 @@ self.hosts_to_rsync = len(item.hosts) self.out.sep("=", txt) self.timestart = item.timestart + self.out.write("Root directory: %s\n" % item.topdir) + roots = [str(i.relto(item.topdir)) for i in item.roots] + self.out.write("To rsync:\n") + for root in roots: + self.out.write(" => %s\n" % root) def report_RsyncFinished(self, item): self.timersync = item.time Modified: py/trunk/py/test/rsession/rsession.py ============================================================================== --- py/trunk/py/test/rsession/rsession.py (original) +++ py/trunk/py/test/rsession/rsession.py Tue Feb 6 19:44:16 2007 @@ -132,7 +132,8 @@ hm.hosts, RemoteReporter) reporter, checkfun = self.wrap_reporter(reporter) - reporter(repevent.TestStarted(hm.hosts)) + reporter(repevent.TestStarted(hm.hosts, self.config.topdir, + hm.roots)) try: nodes = hm.setup_hosts(reporter) @@ -186,7 +187,7 @@ hosts, LocalReporter, args[0]) reporter, checkfun = self.wrap_reporter(reporter) - reporter(repevent.TestStarted(hosts)) + reporter(repevent.TestStarted(hosts, self.config.topdir, [])) colitems = self.config.getcolitems() reporter(repevent.RsyncFinished()) Modified: py/trunk/py/test/rsession/testing/test_reporter.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_reporter.py (original) +++ py/trunk/py/test/rsession/testing/test_reporter.py Tue Feb 6 19:44:16 2007 @@ -142,7 +142,7 @@ rootcol = py.test.collect.Directory(tmpdir) host = HostInfo('localhost') r = self.reporter(config, [host]) - r.report(repevent.TestStarted([host])) + r.report(repevent.TestStarted([host], config.topdir, ["a"])) r.report(repevent.RsyncFinished()) list(rootcol._tryiter(reporterror=lambda x : AbstractSession.reporterror(r.report, x))) r.report(repevent.TestFinished()) @@ -160,7 +160,7 @@ config = py.test.config._reparse([str(tmpdir)]) hosts = [HostInfo(i) for i in ["host1", "host2", "host3"]] r = self.reporter(config, hosts) - r.report(repevent.TestStarted(hosts)) + r.report(repevent.TestStarted(hosts, config.topdir, ["a", "b", "c"])) for host in hosts: r.report(repevent.HostRSyncRoots(host, ["a", "b", "c"])) for host in hosts: Modified: py/trunk/py/test/rsession/testing/test_rest.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_rest.py (original) +++ py/trunk/py/test/rsession/testing/test_rest.py Tue Feb 6 19:44:16 2007 @@ -66,8 +66,9 @@ assert stdout.getvalue() == '::\n\n localhost: READY\n\n' def test_report_TestStarted(self): - event = repevent.TestStarted(hosts=[HostInfo('localhost'), - HostInfo('foo.com')]) + event = repevent.TestStarted([HostInfo('localhost'), + HostInfo('foo.com')], + "aa", ["a", "b"]) reporter.report(event) assert stdout.getvalue() == """\ =========================================== From fijal at codespeak.net Tue Feb 6 19:57:16 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 6 Feb 2007 19:57:16 +0100 (CET) Subject: [py-svn] r38022 - in py/trunk/py/test/rsession: . testing Message-ID: <20070206185716.B5F8210063@code0.codespeak.net> Author: fijal Date: Tue Feb 6 19:57:14 2007 New Revision: 38022 Modified: py/trunk/py/test/rsession/hostmanage.py py/trunk/py/test/rsession/repevent.py py/trunk/py/test/rsession/reporter.py py/trunk/py/test/rsession/rest.py py/trunk/py/test/rsession/testing/test_reporter.py py/trunk/py/test/rsession/testing/test_rest.py py/trunk/py/test/rsession/web.py Log: be a bit more verbose before rsync. Modified: py/trunk/py/test/rsession/hostmanage.py ============================================================================== --- py/trunk/py/test/rsession/hostmanage.py (original) +++ py/trunk/py/test/rsession/hostmanage.py Tue Feb 6 19:57:14 2007 @@ -120,18 +120,17 @@ roots = [self.config.topdir] self.roots = roots - def prepare_gateways(self): + def prepare_gateways(self, reporter): dist_remotepython = self.config.getvalue("dist_remotepython") for host in self.hosts: host.initgateway(python=dist_remotepython) + reporter(repevent.HostGatewayReady(host, self.roots)) host.gw.host = host def init_rsync(self, reporter): # send each rsync root ignores = self.config.getvalue_pathlist("dist_rsync_ignore") - self.prepare_gateways() - for host in self.hosts: - reporter(repevent.HostRSyncRoots(host, self.roots)) + self.prepare_gateways(reporter) for root in self.roots: rsync = HostRSync(ignores=ignores) destrelpath = root.relto(self.config.topdir) Modified: py/trunk/py/test/rsession/repevent.py ============================================================================== --- py/trunk/py/test/rsession/repevent.py (original) +++ py/trunk/py/test/rsession/repevent.py Tue Feb 6 19:57:14 2007 @@ -77,7 +77,7 @@ def __init__(self, host): self.host = host -class HostRSyncRoots(ReportEvent): +class HostGatewayReady(ReportEvent): def __init__(self, host, roots): self.host = host self.roots = roots Modified: py/trunk/py/test/rsession/reporter.py ============================================================================== --- py/trunk/py/test/rsession/reporter.py (original) +++ py/trunk/py/test/rsession/reporter.py Tue Feb 6 19:57:14 2007 @@ -60,8 +60,10 @@ print "%10s: RSYNC ==> %s" % (item.host.hostname[:10], item.host.relpath) - def report_HostRSyncRoots(self, item): + def report_HostGatewayReady(self, item): self.to_rsync[item.host] = len(item.roots) + self.out.write("%10s: gateway initialised (PYTHONPATH = %s)\n"\ + % (item.host.hostname, item.host.gw_remotepath)) def report_HostRSyncRootReady(self, item): self.to_rsync[item.host] -= 1 Modified: py/trunk/py/test/rsession/rest.py ============================================================================== --- py/trunk/py/test/rsession/rest.py (original) +++ py/trunk/py/test/rsession/rest.py Tue Feb 6 19:57:14 2007 @@ -63,6 +63,9 @@ def report_ImmediateFailure(self, item): pass + def report_HostGatewayReady(self, item): + self.to_rsync[item.host] = len(item.roots) + def report_ItemStart(self, event): item = event.item if isinstance(item, py.test.collect.Module): Modified: py/trunk/py/test/rsession/testing/test_reporter.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_reporter.py (original) +++ py/trunk/py/test/rsession/testing/test_reporter.py Tue Feb 6 19:57:14 2007 @@ -162,7 +162,7 @@ r = self.reporter(config, hosts) r.report(repevent.TestStarted(hosts, config.topdir, ["a", "b", "c"])) for host in hosts: - r.report(repevent.HostRSyncRoots(host, ["a", "b", "c"])) + r.report(repevent.HostGatewayReady(host, ["a", "b", "c"])) for host in hosts: for root in ["a", "b", "c"]: r.report(repevent.HostRSyncRootReady(host, root)) Modified: py/trunk/py/test/rsession/testing/test_rest.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_rest.py (original) +++ py/trunk/py/test/rsession/testing/test_rest.py Tue Feb 6 19:57:14 2007 @@ -60,7 +60,7 @@ def test_report_HostRSyncRootReady(self): h = HostInfo('localhost') reporter.hosts_to_rsync = 1 - reporter.report(repevent.HostRSyncRoots(h, ["a"])) + reporter.report(repevent.HostGatewayReady(h, ["a"])) event = repevent.HostRSyncRootReady(h, "a") reporter.report(event) assert stdout.getvalue() == '::\n\n localhost: READY\n\n' Modified: py/trunk/py/test/rsession/web.py ============================================================================== --- py/trunk/py/test/rsession/web.py (original) +++ py/trunk/py/test/rsession/web.py Tue Feb 6 19:57:14 2007 @@ -303,7 +303,7 @@ def _host_ready(self, event): self.pending_events.put(event) - def report_HostRSyncRoots(self, item): + def report_HostGatewayReady(self, item): self.to_rsync[item.host] = len(item.roots) def report_HostRSyncRootReady(self, item): From fijal at codespeak.net Tue Feb 6 20:01:13 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 6 Feb 2007 20:01:13 +0100 (CET) Subject: [py-svn] r38023 - py/trunk/py/test/rsession Message-ID: <20070206190113.7588910063@code0.codespeak.net> Author: fijal Date: Tue Feb 6 20:01:07 2007 New Revision: 38023 Modified: py/trunk/py/test/rsession/hostmanage.py Log: Report HostRSyncing only in case where rsyncing is really performed. Modified: py/trunk/py/test/rsession/hostmanage.py ============================================================================== --- py/trunk/py/test/rsession/hostmanage.py (original) +++ py/trunk/py/test/rsession/hostmanage.py Tue Feb 6 20:01:07 2007 @@ -91,12 +91,14 @@ else: return True - def add_target_host(self, host, destrelpath=None, finishedcallback=None): + def add_target_host(self, host, reporter=lambda x: None, + destrelpath=None, finishedcallback=None): key = host.hostname, host.relpath if key in self._synced: if finishedcallback: finishedcallback() - return False + return False + reporter(repevent.HostRSyncing(host)) self._synced[key] = True # the follow attributes are set from host.initgateway() gw = host.gw @@ -135,10 +137,9 @@ rsync = HostRSync(ignores=ignores) destrelpath = root.relto(self.config.topdir) for host in self.hosts: - reporter(repevent.HostRSyncing(host)) def donecallback(): reporter(repevent.HostRSyncRootReady(host, root)) - rsync.add_target_host(host, destrelpath, + rsync.add_target_host(host, reporter, destrelpath, finishedcallback=donecallback) rsync.send(root) From hpk at codespeak.net Tue Feb 6 20:07:26 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 6 Feb 2007 20:07:26 +0100 (CET) Subject: [py-svn] r38024 - in py/trunk/py/test/rsession: . testing Message-ID: <20070206190726.A12FB10063@code0.codespeak.net> Author: hpk Date: Tue Feb 6 20:06:57 2007 New Revision: 38024 Modified: py/trunk/py/test/rsession/hostmanage.py py/trunk/py/test/rsession/testing/test_hostmanage.py Log: avoid that hostmanage.py tests interfere with the real world (through writing or creating pytestcache-XXX files), semantic change: upon gateway initialization the remote path is not automatically created (this will be done later by rsync anyway) Modified: py/trunk/py/test/rsession/hostmanage.py ============================================================================== --- py/trunk/py/test/rsession/hostmanage.py (original) +++ py/trunk/py/test/rsession/hostmanage.py Tue Feb 6 20:06:57 2007 @@ -37,22 +37,17 @@ gw = py.execnet.SshGateway(self.hostname, remotepython=python) self.gw = gw - channel = gw.remote_exec(py.code.Source(gethomedir, """ + channel = gw.remote_exec(py.code.Source( + gethomedir, + getpath_relto_home, """ import os - targetdir = %r - homedir = gethomedir() - if not os.path.isabs(targetdir): - targetdir = os.path.join(homedir, targetdir) - if not os.path.exists(targetdir): - os.makedirs(targetdir) - os.chdir(homedir) - channel.send(targetdir) - """ % self.relpath)) + os.chdir(gethomedir()) + newdir = getpath_relto_home(%r) + # we intentionally don't ensure that 'newdir' exists + channel.send(newdir) + """ % str(self.relpath) + )) self.gw_remotepath = channel.receive() - #print "initialized", gw, "with remotepath", self.gw_remotepath - if self.hostname == "localhost": - self.localdest = py.path.local(self.gw_remotepath) - assert self.localdest.check(dir=1) def __str__(self): return "" % (self.hostname, self.relpath) @@ -186,3 +181,10 @@ if not homedir: homedir = os.environ.get('HOMEPATH', '.') return homedir + +def getpath_relto_home(targetpath): + import os + if not os.path.isabs(targetpath): + homedir = gethomedir() + targetpath = os.path.join(homedir, targetpath) + return targetpath Modified: py/trunk/py/test/rsession/testing/test_hostmanage.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_hostmanage.py (original) +++ py/trunk/py/test/rsession/testing/test_hostmanage.py Tue Feb 6 20:06:57 2007 @@ -3,8 +3,8 @@ """ import py -from py.__.test.rsession.hostmanage import HostRSync -from py.__.test.rsession.hostmanage import HostInfo, HostManager +from py.__.test.rsession.hostmanage import HostRSync, HostInfo, HostManager +from py.__.test.rsession.hostmanage import gethomedir, getpath_relto_home from py.__.test.rsession import repevent class DirSetup: @@ -14,7 +14,15 @@ self.source = self.tmpdir.ensure("source", dir=1) self.dest = self.tmpdir.join("dest") -class TestHostInfo: +class TestHostInfo(DirSetup): + def _gethostinfo(self, relpath=""): + exampledir = self.tmpdir.join("gethostinfo") + if relpath: + exampledir = exampledir.join(relpath) + assert not exampledir.check() + hostinfo = HostInfo("localhost:%s" % exampledir) + return hostinfo + def test_defaultpath(self): x = HostInfo("localhost") assert x.hostname == "localhost" @@ -39,54 +47,60 @@ host.initgateway) def test_remote_has_homedir_as_currentdir(self): - host = HostInfo("localhost") + host = self._gethostinfo() old = py.path.local.get_temproot().chdir() try: host.initgateway() - channel = host.gw.remote_exec(""" + channel = host.gw.remote_exec(py.code.Source( + gethomedir, """ import os - channel.send(os.getcwd()) - """) - dir = channel.receive() - assert dir == py.path.local._gethomedir() + homedir = gethomedir() + curdir = os.getcwd() + channel.send((curdir, homedir)) + """)) + remote_curdir, remote_homedir = channel.receive() + assert remote_curdir == remote_homedir finally: old.chdir() def test_initgateway_localhost_relpath(self): - name = "pytestcache-localhost" - x = HostInfo("localhost:%s" % name) - x.initgateway() - assert x.gw + host = HostInfo("localhost:somedir") + host.initgateway() + assert host.gw try: homedir = py.path.local._gethomedir() - expected = homedir.join(name) - assert x.gw_remotepath == str(expected) - assert x.localdest == expected + expected = homedir.join("somedir") + assert host.gw_remotepath == str(expected) finally: - x.gw.exit() - + host.gw.exit() def test_initgateway_ssh_and_remotepath(self): option = py.test.config.option if option.sshtarget is None: py.test.skip("no known ssh target, use -S to set one") - x = HostInfo("%s" % (option.sshtarget, )) - x.initgateway() - assert x.gw - assert x.gw_remotepath.endswith(x.relpath) - channel = x.gw.remote_exec(""" + host = HostInfo("%s" % (option.sshtarget, )) + # this test should be careful to not write/rsync anything + # as the remotepath is the default location + # and may be used in the real world + host.initgateway() + assert host.gw + assert host.gw_remotepath.endswith(host.relpath) + channel = host.gw.remote_exec(""" import os homedir = os.environ['HOME'] relpath = channel.receive() path = os.path.join(homedir, relpath) channel.send(path) """) - channel.send(x.relpath) + channel.send(host.relpath) res = channel.receive() - assert res == x.gw_remotepath - assert x.localdest is None + assert res == host.gw_remotepath class TestSyncing(DirSetup): + def _gethostinfo(self): + hostinfo = HostInfo("localhost:%s" % self.dest) + return hostinfo + def test_hrsync_filter(self): self.source.ensure("dir", "file.txt") self.source.ensure(".svn", "entries") @@ -102,7 +116,7 @@ assert 'somedir' in basenames def test_hrsync_one_host(self): - h1 = HostInfo("localhost:%s" % self.dest) + h1 = self._gethostinfo() finished = [] rsync = HostRSync() h1.initgateway() @@ -112,8 +126,8 @@ assert self.dest.join("hello.py").check() def test_hrsync_same_host_twice(self): - h1 = HostInfo("localhost:%s" % self.dest) - h2 = HostInfo("localhost:%s" % self.dest) + h1 = self._gethostinfo() + h2 = self._gethostinfo() finished = [] rsync = HostRSync() l = [] @@ -178,16 +192,7 @@ assert self.dest.join("dir5","file").check() assert not self.dest.join("dir6").check() - def test_hostmanager_rsync_reported_once(self): - py.test.skip("XXX not needed any more") - dir2 = self.source.ensure("dir1", "dir2", dir=1) - dir5 = self.source.ensure("dir5", "dir6", "bogus") - dirf = self.source.ensure("dir3", "file") - config = py.test.config._reparse([self.source]) - hm = HostManager(config, - hosts=[HostInfo("localhost:" + str(self.dest)) - for i in range(3)]) - events = [] - hm.init_rsync(reporter=events.append) - readies = [i for i in events if isinstance(i, repevent.HostReady)] - assert len(readies) == 3 +def test_getpath_relto_home(): + x = getpath_relto_home("hello") + assert x == py.path.local._gethomedir().join("hello") + From fijal at codespeak.net Tue Feb 6 20:11:28 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 6 Feb 2007 20:11:28 +0100 (CET) Subject: [py-svn] r38025 - in py/trunk/py/test/rsession: . webdata Message-ID: <20070206191128.84FB410063@code0.codespeak.net> Author: fijal Date: Tue Feb 6 20:11:12 2007 New Revision: 38025 Modified: py/trunk/py/test/rsession/web.py py/trunk/py/test/rsession/webdata/source.js py/trunk/py/test/rsession/webjs.py Log: Fix a bit web reporter (still not perfect) Modified: py/trunk/py/test/rsession/web.py ============================================================================== --- py/trunk/py/test/rsession/web.py (original) +++ py/trunk/py/test/rsession/web.py Tue Feb 6 20:11:12 2007 @@ -137,6 +137,7 @@ self.stdout = {} self.stderr = {} self.all = 0 + self.to_rsync = {} def findmodule(self, item): # find the most outwards parent which is module Modified: py/trunk/py/test/rsession/webdata/source.js ============================================================================== Binary files. No diff available. Modified: py/trunk/py/test/rsession/webjs.py ============================================================================== --- py/trunk/py/test/rsession/webjs.py (original) +++ py/trunk/py/test/rsession/webjs.py Tue Feb 6 20:11:12 2007 @@ -185,7 +185,7 @@ host_elem.childNodes[0].nodeValue = '%s[%s]' % ( glob.host_dict[msg['hostkey']], count) - elif msg['type'] == 'HostReady': + elif msg['type'] == 'HostRSyncRootReady': host_elem = dom.document.getElementById(msg['hostkey']) host_elem.style.background = \ "#00ff00" From hpk at codespeak.net Tue Feb 6 20:15:35 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 6 Feb 2007 20:15:35 +0100 (CET) Subject: [py-svn] r38027 - py/trunk/py/test/rsession Message-ID: <20070206191535.A8D3210063@code0.codespeak.net> Author: hpk Date: Tue Feb 6 20:15:33 2007 New Revision: 38027 Modified: py/trunk/py/test/rsession/reporter.py Log: more precise reporting to the user Modified: py/trunk/py/test/rsession/reporter.py ============================================================================== --- py/trunk/py/test/rsession/reporter.py (original) +++ py/trunk/py/test/rsession/reporter.py Tue Feb 6 20:15:33 2007 @@ -62,7 +62,7 @@ def report_HostGatewayReady(self, item): self.to_rsync[item.host] = len(item.roots) - self.out.write("%10s: gateway initialised (PYTHONPATH = %s)\n"\ + self.out.write("%10s: gateway initialised (remote topdir: %s)\n"\ % (item.host.hostname, item.host.gw_remotepath)) def report_HostRSyncRootReady(self, item): @@ -84,11 +84,12 @@ self.hosts_to_rsync = len(item.hosts) self.out.sep("=", txt) self.timestart = item.timestart - self.out.write("Root directory: %s\n" % item.topdir) + self.out.write("local top directory: %s\n" % item.topdir) roots = [str(i.relto(item.topdir)) for i in item.roots] - self.out.write("To rsync:\n") - for root in roots: - self.out.write(" => %s\n" % root) + for i, root in py.builtin.enumerate(roots): + outof = "%d/%d" %(i+1, len(roots)) + self.out.write("local RSync root [%s] %s\n" % + (outof, root)) def report_RsyncFinished(self, item): self.timersync = item.time From hpk at codespeak.net Tue Feb 6 20:32:22 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 6 Feb 2007 20:32:22 +0100 (CET) Subject: [py-svn] r38028 - in py/trunk/py/execnet: . testing Message-ID: <20070206193222.7B4AD10063@code0.codespeak.net> Author: hpk Date: Tue Feb 6 20:32:21 2007 New Revision: 38028 Modified: py/trunk/py/execnet/rsync.py py/trunk/py/execnet/testing/test_rsync.py Log: make rsync configurable regarding its output (verbose option and overridable method) Modified: py/trunk/py/execnet/rsync.py ============================================================================== --- py/trunk/py/execnet/rsync.py (original) +++ py/trunk/py/execnet/rsync.py Tue Feb 6 20:32:21 2007 @@ -15,10 +15,11 @@ symlinks will be just copied (regardless of existance of such a path on remote side). """ - def __init__(self, callback=None, **options): + def __init__(self, callback=None, verbose=True, **options): for name in options: assert name in ('delete') self._options = options + self._verbose = verbose assert callback is None or callable(callback) self._callback = callback self._channels = {} @@ -84,11 +85,13 @@ # ! there is a reason for the interning: # sharing multiple copies of the file's data data = intern(data) - print '%s <= %s' % ( - channel.gateway.remoteaddress, - modified_rel_path) + self._report_send_file(channel.gateway, modified_rel_path) channel.send(data) + def _report_send_file(self, gateway, modified_rel_path): + if self._verbose: + print '%s <= %s' % (gateway.remoteaddress, modified_rel_path) + def send(self, sourcedir): """ Sends a sourcedir to all added targets. """ Modified: py/trunk/py/execnet/testing/test_rsync.py ============================================================================== --- py/trunk/py/execnet/testing/test_rsync.py (original) +++ py/trunk/py/execnet/testing/test_rsync.py Tue Feb 6 20:32:21 2007 @@ -69,6 +69,31 @@ assert self.dest2.join('hello').check() py.test.raises(IOError, "rsync.send(self.source)") + def test_rsync_default_reporting(self): + source = self.source + source.ensure("hello") + cap = py.io.StdCapture() + try: + rsync = RSync() + rsync.add_target(gw, self.dest1) + rsync.send(self.source) + finally: + out, err = cap.reset() + assert out.find("hello") != -1 + + def test_rsync_non_verbose(self): + source = self.source + source.ensure("hello") + cap = py.io.StdCapture() + try: + rsync = RSync(verbose=False) + rsync.add_target(gw, self.dest1) + rsync.send(self.source) + finally: + out, err = cap.reset() + assert not out + assert not err + def test_symlink_rsync(self): if py.std.sys.platform == 'win32': py.test.skip("symlinks are unsupported on Windows.") From fijal at codespeak.net Tue Feb 6 21:06:03 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 6 Feb 2007 21:06:03 +0100 (CET) Subject: [py-svn] r38029 - py/trunk/py/test/rsession/testing Message-ID: <20070206200603.6842F10070@code0.codespeak.net> Author: fijal Date: Tue Feb 6 21:05:55 2007 New Revision: 38029 Added: py/trunk/py/test/rsession/testing/runtest.py Modified: py/trunk/py/test/rsession/testing/test_slave.py Log: Move funcxxxspec out of pylib itself. Added: py/trunk/py/test/rsession/testing/runtest.py ============================================================================== --- (empty file) +++ py/trunk/py/test/rsession/testing/runtest.py Tue Feb 6 21:05:55 2007 @@ -0,0 +1,54 @@ + +""" Support module for running tests +""" + +import py + +def func_source(): + import py + import time + def funcpass(): + pass + + def funcfail(): + raise AssertionError("hello world") + + def funcskip(): + py.test.skip("skipped") + + def funcprint(): + print "samfing" + + def funcprintfail(): + print "samfing elz" + asddsa + + def funcoptioncustom(): + assert py.test.config.getvalue("custom") + + def funchang(): + import time + time.sleep(1000) + +class BasicRsessionTest(object): + def setup_class(cls): + tmptop = py.test.ensuretemp("test_suite") + name = cls.__name__ + tmpdir = tmptop.ensure(name, dir=1) + source = py.code.Source(func_source)[1:].deindent() + tmpdir.ensure("test_one.py").write(source) + tmpdir.ensure("__init__.py") + cls.rootdir = tmpdir + cls.config = py.test.config._reparse([cls.rootdir]) + cls.rootcol = cls.config._getcollector(tmpdir) + #cls.rootcol._config = cls.config + BASE = "test_one.py/" + cls.funcpass_spec = (BASE + "funcpass").split("/") + cls.funcfail_spec = (BASE + "funcfail").split("/") + cls.funcskip_spec = (BASE + "funcskip").split("/") + cls.funcprint_spec = (BASE + "funcprint").split("/") + cls.funcprintfail_spec = (BASE + "funcprintfail").split("/") + cls.funcoptioncustom_spec = (BASE + "funcoptioncustom").split("/") + cls.funchang_spec = (BASE + "funchang").split("/") + cls.mod_spec = BASE[:-1].split("/") + Modified: py/trunk/py/test/rsession/testing/test_slave.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_slave.py (original) +++ py/trunk/py/test/rsession/testing/test_slave.py Tue Feb 6 21:05:55 2007 @@ -3,6 +3,7 @@ from py.__.test.rsession.slave import SlaveNode, slave_main, setup, PidInfo from py.__.test.rsession.outcome import ReprOutcome import py, sys +from py.__.test.rsession.testing.runtest import BasicRsessionTest modlevel = [] import os @@ -10,108 +11,69 @@ if sys.platform == 'win32': py.test.skip("rsession is unsupported on Windows.") -def setup_module(module): - module.tmpdir = py.test.ensuretemp(module.__name__) - module.rootdir = py.path.local(py.__file__).dirpath().dirpath() - module.rootcol = py.test.collect.Directory(rootdir) - -# ---------------------------------------------------------------------- -# inlined testing functions used below -def funcpass(): - pass - -def funcfail(): - raise AssertionError("hello world") - -def funcskip(): - py.test.skip("skipped") - -def funcprint(): - print "samfing" - -def funcprintfail(): - print "samfing elz" - asddsa - -def funcoptioncustom(): - assert py.test.config.getvalue("custom") - -def funchang(): - import time - time.sleep(1000) - -BASE = "py/test/rsession/testing/test_slave.py/" -funcpass_spec = (BASE + "funcpass").split("/") -funcfail_spec = (BASE + "funcfail").split("/") -funcskip_spec = (BASE + "funcskip").split("/") -funcprint_spec = (BASE + "funcprint").split("/") -funcprintfail_spec = (BASE + "funcprintfail").split("/") -funcoptioncustom_spec = (BASE + "funcoptioncustom").split("/") -funchang_spec = (BASE + "funchang").split("/") -mod_spec = BASE[:-1].split("/") - # ---------------------------------------------------------------------- from py.__.test.rsession.executor import RunExecutor -def gettestnode(): - config = py.test.config._reparse([rootdir]) - pidinfo = PidInfo() - node = SlaveNode(config, pidinfo, executor=RunExecutor) - return node - -def test_slave_run_passing(): - node = gettestnode() - item = rootcol._getitembynames(funcpass_spec) - outcome = node.execute(item._get_collector_trail()) - assert outcome.passed - assert not outcome.setupfailure - - ser = outcome.make_repr() - reproutcome = ReprOutcome(ser) - assert reproutcome.passed - assert not reproutcome.setupfailure - -def test_slave_run_failing(): - node = gettestnode() - item = rootcol._getitembynames(funcfail_spec) - outcome = node.execute(item._get_collector_trail()) - assert not outcome.passed - assert not outcome.setupfailure - assert len(outcome.excinfo.traceback) == 1 - assert outcome.excinfo.traceback[-1].frame.code.name == 'funcfail' - - ser = outcome.make_repr() - reproutcome = ReprOutcome(ser) - assert not reproutcome.passed - assert not reproutcome.setupfailure - assert reproutcome.excinfo +class TestSlave(BasicRsessionTest): + def gettestnode(self): + pidinfo = PidInfo() + node = SlaveNode(self.config, pidinfo, executor=RunExecutor) + return node + + def test_slave_run_passing(self): + node = self.gettestnode() + item = self.rootcol._getitembynames(self.funcpass_spec) + outcome = node.execute(item._get_collector_trail()) + assert outcome.passed + assert not outcome.setupfailure + + ser = outcome.make_repr() + reproutcome = ReprOutcome(ser) + assert reproutcome.passed + assert not reproutcome.setupfailure + + def test_slave_run_failing(self): + node = self.gettestnode() + item = self.rootcol._getitembynames(self.funcfail_spec) + outcome = node.execute(item._get_collector_trail()) + assert not outcome.passed + assert not outcome.setupfailure + assert len(outcome.excinfo.traceback) == 1 + assert outcome.excinfo.traceback[-1].frame.code.name == 'funcfail' + + ser = outcome.make_repr() + reproutcome = ReprOutcome(ser) + assert not reproutcome.passed + assert not reproutcome.setupfailure + assert reproutcome.excinfo -def test_slave_run_skipping(): - node = gettestnode() - item = rootcol._getitembynames(funcskip_spec) - outcome = node.execute(item._get_collector_trail()) - assert not outcome.passed - assert outcome.skipped - - ser = outcome.make_repr() - reproutcome = ReprOutcome(ser) - assert not reproutcome.passed - assert reproutcome.skipped - -def test_slave_run_failing_wrapped(): - node = gettestnode() - item = rootcol._getitembynames(funcfail_spec) - repr_outcome = node.run(item._get_collector_trail()) - outcome = ReprOutcome(repr_outcome) - assert not outcome.passed - assert not outcome.setupfailure - assert outcome.excinfo - -def test_slave_run_different_stuff(): - node = gettestnode() - node.run(rootcol._getitembynames("py doc log.txt".split()). - _get_collector_trail()) + def test_slave_run_skipping(self): + node = self.gettestnode() + item = self.rootcol._getitembynames(self.funcskip_spec) + outcome = node.execute(item._get_collector_trail()) + assert not outcome.passed + assert outcome.skipped + + ser = outcome.make_repr() + reproutcome = ReprOutcome(ser) + assert not reproutcome.passed + assert reproutcome.skipped + + def test_slave_run_failing_wrapped(self): + node = self.gettestnode() + item = self.rootcol._getitembynames(self.funcfail_spec) + repr_outcome = node.run(item._get_collector_trail()) + outcome = ReprOutcome(repr_outcome) + assert not outcome.passed + assert not outcome.setupfailure + assert outcome.excinfo + + def test_slave_run_different_stuff(self): + py.test.skip("XXX not this way") + node = self.gettestnode() + node.run(self.rootcol._getitembynames("py doc log.txt".split()). + _get_collector_trail()) def test_pidinfo(): if not hasattr(os, 'fork') or not hasattr(os, 'waitpid'): From fijal at codespeak.net Tue Feb 6 21:14:05 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 6 Feb 2007 21:14:05 +0100 (CET) Subject: [py-svn] r38030 - py/trunk/py/test/rsession/testing Message-ID: <20070206201405.E3CA710070@code0.codespeak.net> Author: fijal Date: Tue Feb 6 21:13:57 2007 New Revision: 38030 Modified: py/trunk/py/test/rsession/testing/test_executor.py Log: Refactor this test to use new testing service Modified: py/trunk/py/test/rsession/testing/test_executor.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_executor.py (original) +++ py/trunk/py/test/rsession/testing/test_executor.py Tue Feb 6 21:13:57 2007 @@ -5,47 +5,11 @@ from py.__.test.rsession.executor import RunExecutor, BoxExecutor,\ AsyncExecutor, ApigenExecutor from py.__.test.rsession.outcome import ReprOutcome -from py.__.test.rsession.testing.test_slave import funcprint_spec, \ - funcprintfail_spec +from py.__.test.rsession.testing.runtest import BasicRsessionTest -def setup_module(mod): - mod.rootdir = py.path.local(py.__file__).dirpath().dirpath() - mod.config = py.test.config._reparse([mod.rootdir]) - -def XXXtest_executor_passing_function(): - ex = Executor(example1.f1) - outcome = ex.execute() - assert outcome.passed - -def XXXtest_executor_raising_function(): - ex = Executor(example1.g1) - outcome = ex.execute() - assert not outcome.passed - excinfo = outcome.excinfo - assert excinfo.type == ValueError - -def XXXtest_executor_traceback(): - ex = Executor(example1.g1) - outcome = ex.execute() - excinfo = outcome.excinfo - assert len(excinfo.traceback) == 2 - assert excinfo.traceback[1].frame.code.name == 'g2' - assert excinfo.traceback[0].frame.code.name == 'g1' - -def XXX_test_executor_setup_passing(): - ex = Executor(example1.f1, setup=lambda: None) - outcome = ex.execute() - assert outcome.passed - assert not outcome.setupfailure - -def XXX_test_executor_setup_failing(): - def failingsetup(): - raise ValueError - ex = Executor(example1.f1, setup=failingsetup) - outcome = ex.execute() - assert not outcome.passed - assert outcome.setupfailure - assert outcome.excinfo.traceback[-1].frame.code.name == 'failingsetup' +#def setup_module(mod): +# mod.rootdir = py.path.local(py.__file__).dirpath().dirpath() +# mod.config = py.test.config._reparse([mod.rootdir]) class ItemTestPassing(py.test.Item): def run(self): @@ -59,112 +23,111 @@ def run(self): py.test.skip("hello") -def test_run_executor(): - ex = RunExecutor(ItemTestPassing("pass"), config=config) - outcome = ex.execute() - assert outcome.passed +class TestExecutor(BasicRsessionTest): + def test_run_executor(self): + ex = RunExecutor(ItemTestPassing("pass"), config=self.config) + outcome = ex.execute() + assert outcome.passed - ex = RunExecutor(ItemTestFailing("fail"), config=config) - outcome = ex.execute() - assert not outcome.passed - - ex = RunExecutor(ItemTestSkipping("skip"), config=config) - outcome = ex.execute() - assert outcome.skipped - assert not outcome.passed - assert not outcome.excinfo - -def test_box_executor(): - ex = BoxExecutor(ItemTestPassing("pass"), config=config) - outcome_repr = ex.execute() - outcome = ReprOutcome(outcome_repr) - assert outcome.passed + ex = RunExecutor(ItemTestFailing("fail"), config=self.config) + outcome = ex.execute() + assert not outcome.passed + + ex = RunExecutor(ItemTestSkipping("skip"), config=self.config) + outcome = ex.execute() + assert outcome.skipped + assert not outcome.passed + assert not outcome.excinfo + + def test_box_executor(self): + ex = BoxExecutor(ItemTestPassing("pass"), config=self.config) + outcome_repr = ex.execute() + outcome = ReprOutcome(outcome_repr) + assert outcome.passed - ex = BoxExecutor(ItemTestFailing("fail"), config=config) - outcome_repr = ex.execute() - outcome = ReprOutcome(outcome_repr) - assert not outcome.passed - - ex = BoxExecutor(ItemTestSkipping("skip"), config=config) - outcome_repr = ex.execute() - outcome = ReprOutcome(outcome_repr) - assert outcome.skipped - assert not outcome.passed - assert not outcome.excinfo - -def test_box_executor_stdout(): - rootcol = py.test.collect.Directory(rootdir) - item = rootcol._getitembynames(funcprint_spec) - ex = BoxExecutor(item, config=config) - outcome_repr = ex.execute() - outcome = ReprOutcome(outcome_repr) - assert outcome.passed - assert outcome.stdout.find("samfing") != -1 - -def test_box_executor_stdout_error(): - rootcol = py.test.collect.Directory(rootdir) - item = rootcol._getitembynames(funcprintfail_spec) - ex = BoxExecutor(item, config=config) - outcome_repr = ex.execute() - outcome = ReprOutcome(outcome_repr) - assert not outcome.passed - assert outcome.stdout.find("samfing elz") != -1 - -def test_cont_executor(): - rootcol = py.test.collect.Directory(rootdir) - item = rootcol._getitembynames(funcprintfail_spec) - ex = AsyncExecutor(item, config=config) - cont, pid = ex.execute() - assert pid - outcome_repr = cont() - outcome = ReprOutcome(outcome_repr) - assert not outcome.passed - assert outcome.stdout.find("samfing elz") != -1 - -def test_apigen_executor(): - class Tracer(object): - def __init__(self): - self.starts = 0 - self.ends = 0 + ex = BoxExecutor(ItemTestFailing("fail"), config=self.config) + outcome_repr = ex.execute() + outcome = ReprOutcome(outcome_repr) + assert not outcome.passed + + ex = BoxExecutor(ItemTestSkipping("skip"), config=self.config) + outcome_repr = ex.execute() + outcome = ReprOutcome(outcome_repr) + assert outcome.skipped + assert not outcome.passed + assert not outcome.excinfo + + def test_box_executor_stdout(self): + item = self.rootcol._getitembynames(self.funcprint_spec) + ex = BoxExecutor(item, config=self.config) + outcome_repr = ex.execute() + outcome = ReprOutcome(outcome_repr) + assert outcome.passed + assert outcome.stdout.find("samfing") != -1 + + def test_box_executor_stdout_error(self): + item = self.rootcol._getitembynames(self.funcprintfail_spec) + ex = BoxExecutor(item, config=self.config) + outcome_repr = ex.execute() + outcome = ReprOutcome(outcome_repr) + assert not outcome.passed + assert outcome.stdout.find("samfing elz") != -1 + + def test_cont_executor(self): + item = self.rootcol._getitembynames(self.funcprintfail_spec) + ex = AsyncExecutor(item, config=self.config) + cont, pid = ex.execute() + assert pid + outcome_repr = cont() + outcome = ReprOutcome(outcome_repr) + assert not outcome.passed + assert outcome.stdout.find("samfing elz") != -1 + + def test_apigen_executor(self): + class Tracer(object): + def __init__(self): + self.starts = 0 + self.ends = 0 - def start_tracing(self): - self.starts += 1 + def start_tracing(self): + self.starts += 1 - def end_tracing(self): - self.ends += 1 + def end_tracing(self): + self.ends += 1 - tmpdir = py.test.ensuretemp("apigen_executor") - tmpdir.ensure("__init__.py") - tmpdir.ensure("test_one.py").write(py.code.Source(""" - def g(): - pass + tmpdir = py.test.ensuretemp("apigen_executor") + tmpdir.ensure("__init__.py") + tmpdir.ensure("test_one.py").write(py.code.Source(""" + def g(): + pass - def test_1(): - g() + def test_1(): + g() - class TestX(object): - def setup_method(self, m): - self.ttt = 1 - - def test_one(self): - self.ttt += 1 - - def test_raise(self): - 1/0 - """)) - rootcol = py.test.collect.Directory(tmpdir) - tracer = Tracer() - item = rootcol._getitembynames("test_one.py/test_1") - ex = ApigenExecutor(item, config=config) - out1 = ex.execute(tracer) - item = rootcol._getitembynames("test_one.py/TestX/()/test_one") - ex = ApigenExecutor(item, config=config) - out2 = ex.execute(tracer) - item = rootcol._getitembynames("test_one.py/TestX/()/test_raise") - ex = ApigenExecutor(item, config=config) - out3 = ex.execute(tracer) - assert tracer.starts == 3 - assert tracer.ends == 3 - assert out1.passed - assert out2.passed - assert not out3.passed + class TestX(object): + def setup_method(self, m): + self.ttt = 1 + + def test_one(self): + self.ttt += 1 + + def test_raise(self): + 1/0 + """)) + config = py.test.config._reparse([tmpdir]) + rootcol = config._getcollector(tmpdir) + tracer = Tracer() + item = rootcol._getitembynames("test_one.py/test_1") + ex = ApigenExecutor(item, config=config) + out1 = ex.execute(tracer) + item = rootcol._getitembynames("test_one.py/TestX/()/test_one") + ex = ApigenExecutor(item, config=config) + out2 = ex.execute(tracer) + item = rootcol._getitembynames("test_one.py/TestX/()/test_raise") + ex = ApigenExecutor(item, config=config) + out3 = ex.execute(tracer) + assert tracer.starts == 3 + assert tracer.ends == 3 + assert out1.passed + assert out2.passed + assert not out3.passed From fijal at codespeak.net Tue Feb 6 21:21:15 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 6 Feb 2007 21:21:15 +0100 (CET) Subject: [py-svn] r38031 - py/trunk/py/test/rsession/testing Message-ID: <20070206202115.95BF410070@code0.codespeak.net> Author: fijal Date: Tue Feb 6 21:21:09 2007 New Revision: 38031 Modified: py/trunk/py/test/rsession/testing/test_reporter.py py/trunk/py/test/rsession/testing/test_rest.py py/trunk/py/test/rsession/testing/test_rsession.py Log: refactor rest of tests Modified: py/trunk/py/test/rsession/testing/test_reporter.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_reporter.py (original) +++ py/trunk/py/test/rsession/testing/test_reporter.py Tue Feb 6 21:21:09 2007 @@ -23,10 +23,9 @@ RemoteReporter from py.__.test.rsession import repevent from py.__.test.rsession.outcome import ReprOutcome, Outcome -from py.__.test.rsession.testing.test_slave import funcpass_spec, mod_spec from py.__.test.rsession.hostmanage import HostInfo from py.__.test.rsession.box import Box -#from py.__.test. +from py.__.test.rsession.testing.runtest import BasicRsessionTest import sys from StringIO import StringIO @@ -38,10 +37,7 @@ def __init__(self, host): self.gateway = DummyGateway(host) -class AbstractTestReporter(object): - def setup_class(cls): - cls.pkgdir = py.path.local(py.__file__).dirpath() - +class AbstractTestReporter(BasicRsessionTest): def prepare_outcomes(self): # possible outcomes try: @@ -61,10 +57,7 @@ return outcomes def report_received_item_outcome(self): - config = py.test.config._reparse(["some_sub"]) - # we just go... - rootcol = py.test.collect.Directory(self.pkgdir.dirpath()) - item = rootcol._getitembynames(funcpass_spec) + item = self.rootcol._getitembynames(self.funcpass_spec) outcomes = self.prepare_outcomes() def boxfun(config, item, outcomes): @@ -75,30 +68,26 @@ r.report(repevent.ReceivedItemOutcome(ch, item, outcome)) cap = py.io.StdCaptureFD() - boxfun(config, item, outcomes) + boxfun(self.config, item, outcomes) out, err = cap.reset() assert not err return out def _test_module(self): - config = py.test.config._reparse(["some_sub"]) - # we just go... - rootcol = py.test.collect.Directory(self.pkgdir.dirpath()) - funcitem = rootcol._getitembynames(funcpass_spec) - moditem = rootcol._getitembynames(mod_spec) + funcitem = self.rootcol._getitembynames(self.funcpass_spec) + moditem = self.rootcol._getitembynames(self.mod_spec) outcomes = self.prepare_outcomes() - def boxfun(pkgdir, config, item, funcitem, outcomes): + def boxfun(config, item, funcitem, outcomes): hosts = [HostInfo('localhost')] r = self.reporter(config, hosts) - #r.pkgdir = pkdgir r.report(repevent.ItemStart(item)) ch = DummyChannel(hosts[0]) for outcome in outcomes: r.report(repevent.ReceivedItemOutcome(ch, funcitem, outcome)) cap = py.io.StdCaptureFD() - boxfun(self.pkgdir, config, moditem, funcitem, outcomes) + boxfun(self.config, moditem, funcitem, outcomes) out, err = cap.reset() assert not err return out @@ -185,7 +174,7 @@ def test_module(self): #py.test.skip("XXX rewrite test to not rely on exact formatting") output = self._test_module() - assert output.find("test_slave") != -1 + assert output.find("test_one") != -1 assert output.endswith("FsF."), output def test_full_module(self): Modified: py/trunk/py/test/rsession/testing/test_rest.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_rest.py (original) +++ py/trunk/py/test/rsession/testing/test_rest.py Tue Feb 6 21:21:09 2007 @@ -335,6 +335,7 @@ py.test.skip("Not implemented") def test_report_received_item_outcome(self): + py.test.skip("Relying on exact output matching") val = self.report_received_item_outcome() expected = """\ * localhost\: **FAILED** `traceback0`_\n py/test/rsession/testing/test\_slave.py/funcpass Modified: py/trunk/py/test/rsession/testing/test_rsession.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_rsession.py (original) +++ py/trunk/py/test/rsession/testing/test_rsession.py Tue Feb 6 21:21:09 2007 @@ -6,11 +6,8 @@ from py.__.test.rsession import repevent from py.__.test.rsession.rsession import RSession from py.__.test.rsession.hostmanage import HostManager, HostInfo -from py.__.test.rsession.testing.test_slave import funcfail_spec,\ - funcpass_spec, funcskip_spec, funcprint_spec, funcprintfail_spec, \ - funcoptioncustom_spec - -from test_hostmanage import DirSetup +from py.__.test.rsession.testing.runtest import BasicRsessionTest +from py.__.test.rsession.testing.test_hostmanage import DirSetup def setup_module(mod): mod.pkgdir = py.path.local(py.__file__).dirpath() @@ -32,7 +29,7 @@ assert len(events) == 2 assert str(events[1][0].value) == "Reason" -class TestRSessionRemote(DirSetup): +class TestRSessionRemote(DirSetup, BasicRsessionTest): def test_example_distribution_minus_x(self): self.source.ensure("sub", "conftest.py").write(py.code.Source(""" dist_hosts = ['localhost:%s'] @@ -145,18 +142,16 @@ hosts = [HostInfo('localhost')] allevents = [] - config = py.test.config._reparse([]) - hm = HostManager(config, hosts=hosts) + hm = HostManager(self.config, hosts=hosts) nodes = hm.setup_hosts(allevents.append) from py.__.test.rsession.testing.test_executor \ import ItemTestPassing, ItemTestFailing, ItemTestSkipping - rootcol = py.test.collect.Directory(pkgdir.dirpath()) - itempass = rootcol._getitembynames(funcpass_spec) - itemfail = rootcol._getitembynames(funcfail_spec) - itemskip = rootcol._getitembynames(funcskip_spec) - itemprint = rootcol._getitembynames(funcprint_spec) + itempass = self.rootcol._getitembynames(self.funcpass_spec) + itemfail = self.rootcol._getitembynames(self.funcfail_spec) + itemskip = self.rootcol._getitembynames(self.funcskip_spec) + itemprint = self.rootcol._getitembynames(self.funcprint_spec) # actually run some tests for node in nodes: From hpk at codespeak.net Tue Feb 6 21:28:08 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 6 Feb 2007 21:28:08 +0100 (CET) Subject: [py-svn] r38032 - py/trunk/py/test/rsession/testing Message-ID: <20070206202808.9FE1E10070@code0.codespeak.net> Author: hpk Date: Tue Feb 6 21:28:07 2007 New Revision: 38032 Added: py/trunk/py/test/rsession/testing/basetest.py - copied, changed from r38029, py/trunk/py/test/rsession/testing/runtest.py Removed: py/trunk/py/test/rsession/testing/runtest.py Modified: py/trunk/py/test/rsession/testing/test_slave.py Log: simplified testing machinery a bit (you know just have to add an example and can immediately use it from tests without adding boilerplate anywhere) Copied: py/trunk/py/test/rsession/testing/basetest.py (from r38029, py/trunk/py/test/rsession/testing/runtest.py) ============================================================================== --- py/trunk/py/test/rsession/testing/runtest.py (original) +++ py/trunk/py/test/rsession/testing/basetest.py Tue Feb 6 21:28:07 2007 @@ -32,23 +32,15 @@ class BasicRsessionTest(object): def setup_class(cls): - tmptop = py.test.ensuretemp("test_suite") - name = cls.__name__ - tmpdir = tmptop.ensure(name, dir=1) + tmpdir = py.test.ensuretemp(cls.__name__) source = py.code.Source(func_source)[1:].deindent() - tmpdir.ensure("test_one.py").write(source) - tmpdir.ensure("__init__.py") - cls.rootdir = tmpdir - cls.config = py.test.config._reparse([cls.rootdir]) - cls.rootcol = cls.config._getcollector(tmpdir) - #cls.rootcol._config = cls.config - BASE = "test_one.py/" - cls.funcpass_spec = (BASE + "funcpass").split("/") - cls.funcfail_spec = (BASE + "funcfail").split("/") - cls.funcskip_spec = (BASE + "funcskip").split("/") - cls.funcprint_spec = (BASE + "funcprint").split("/") - cls.funcprintfail_spec = (BASE + "funcprintfail").split("/") - cls.funcoptioncustom_spec = (BASE + "funcoptioncustom").split("/") - cls.funchang_spec = (BASE + "funchang").split("/") - cls.mod_spec = BASE[:-1].split("/") + testonepath = tmpdir.ensure("test_one.py") + testonepath.write(source) + cls.config = py.test.config._reparse([tmpdir]) + cls.collector_test_one = cls.config._getcollector(testonepath) + def getexample(self, name): + funcname = "func" + name + col = self.collector_test_one.join(funcname) + assert col is not None, funcname + return col Deleted: /py/trunk/py/test/rsession/testing/runtest.py ============================================================================== --- /py/trunk/py/test/rsession/testing/runtest.py Tue Feb 6 21:28:07 2007 +++ (empty file) @@ -1,54 +0,0 @@ - -""" Support module for running tests -""" - -import py - -def func_source(): - import py - import time - def funcpass(): - pass - - def funcfail(): - raise AssertionError("hello world") - - def funcskip(): - py.test.skip("skipped") - - def funcprint(): - print "samfing" - - def funcprintfail(): - print "samfing elz" - asddsa - - def funcoptioncustom(): - assert py.test.config.getvalue("custom") - - def funchang(): - import time - time.sleep(1000) - -class BasicRsessionTest(object): - def setup_class(cls): - tmptop = py.test.ensuretemp("test_suite") - name = cls.__name__ - tmpdir = tmptop.ensure(name, dir=1) - source = py.code.Source(func_source)[1:].deindent() - tmpdir.ensure("test_one.py").write(source) - tmpdir.ensure("__init__.py") - cls.rootdir = tmpdir - cls.config = py.test.config._reparse([cls.rootdir]) - cls.rootcol = cls.config._getcollector(tmpdir) - #cls.rootcol._config = cls.config - BASE = "test_one.py/" - cls.funcpass_spec = (BASE + "funcpass").split("/") - cls.funcfail_spec = (BASE + "funcfail").split("/") - cls.funcskip_spec = (BASE + "funcskip").split("/") - cls.funcprint_spec = (BASE + "funcprint").split("/") - cls.funcprintfail_spec = (BASE + "funcprintfail").split("/") - cls.funcoptioncustom_spec = (BASE + "funcoptioncustom").split("/") - cls.funchang_spec = (BASE + "funchang").split("/") - cls.mod_spec = BASE[:-1].split("/") - Modified: py/trunk/py/test/rsession/testing/test_slave.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_slave.py (original) +++ py/trunk/py/test/rsession/testing/test_slave.py Tue Feb 6 21:28:07 2007 @@ -3,7 +3,7 @@ from py.__.test.rsession.slave import SlaveNode, slave_main, setup, PidInfo from py.__.test.rsession.outcome import ReprOutcome import py, sys -from py.__.test.rsession.testing.runtest import BasicRsessionTest +from py.__.test.rsession.testing.basetest import BasicRsessionTest modlevel = [] import os @@ -23,7 +23,7 @@ def test_slave_run_passing(self): node = self.gettestnode() - item = self.rootcol._getitembynames(self.funcpass_spec) + item = self.getexample("pass") outcome = node.execute(item._get_collector_trail()) assert outcome.passed assert not outcome.setupfailure @@ -35,7 +35,7 @@ def test_slave_run_failing(self): node = self.gettestnode() - item = self.rootcol._getitembynames(self.funcfail_spec) + item = self.getexample("fail") outcome = node.execute(item._get_collector_trail()) assert not outcome.passed assert not outcome.setupfailure @@ -50,7 +50,7 @@ def test_slave_run_skipping(self): node = self.gettestnode() - item = self.rootcol._getitembynames(self.funcskip_spec) + item = self.getexample("skip") outcome = node.execute(item._get_collector_trail()) assert not outcome.passed assert outcome.skipped @@ -62,7 +62,7 @@ def test_slave_run_failing_wrapped(self): node = self.gettestnode() - item = self.rootcol._getitembynames(self.funcfail_spec) + item = self.getexample("fail") repr_outcome = node.run(item._get_collector_trail()) outcome = ReprOutcome(repr_outcome) assert not outcome.passed From fijal at codespeak.net Tue Feb 6 21:36:05 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 6 Feb 2007 21:36:05 +0100 (CET) Subject: [py-svn] r38033 - py/trunk/py/test/rsession/testing Message-ID: <20070206203605.9C9F010070@code0.codespeak.net> Author: fijal Date: Tue Feb 6 21:36:03 2007 New Revision: 38033 Modified: py/trunk/py/test/rsession/testing/basetest.py py/trunk/py/test/rsession/testing/test_executor.py py/trunk/py/test/rsession/testing/test_reporter.py py/trunk/py/test/rsession/testing/test_rsession.py Log: Fix tests once again Modified: py/trunk/py/test/rsession/testing/basetest.py ============================================================================== --- py/trunk/py/test/rsession/testing/basetest.py (original) +++ py/trunk/py/test/rsession/testing/basetest.py Tue Feb 6 21:36:03 2007 @@ -44,3 +44,6 @@ col = self.collector_test_one.join(funcname) assert col is not None, funcname return col + + def getmod(self): + return self.collector_test_one Modified: py/trunk/py/test/rsession/testing/test_executor.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_executor.py (original) +++ py/trunk/py/test/rsession/testing/test_executor.py Tue Feb 6 21:36:03 2007 @@ -5,7 +5,7 @@ from py.__.test.rsession.executor import RunExecutor, BoxExecutor,\ AsyncExecutor, ApigenExecutor from py.__.test.rsession.outcome import ReprOutcome -from py.__.test.rsession.testing.runtest import BasicRsessionTest +from py.__.test.rsession.testing.basetest import BasicRsessionTest #def setup_module(mod): # mod.rootdir = py.path.local(py.__file__).dirpath().dirpath() @@ -58,7 +58,7 @@ assert not outcome.excinfo def test_box_executor_stdout(self): - item = self.rootcol._getitembynames(self.funcprint_spec) + item = self.getexample("print") ex = BoxExecutor(item, config=self.config) outcome_repr = ex.execute() outcome = ReprOutcome(outcome_repr) @@ -66,7 +66,7 @@ assert outcome.stdout.find("samfing") != -1 def test_box_executor_stdout_error(self): - item = self.rootcol._getitembynames(self.funcprintfail_spec) + item = self.getexample("printfail") ex = BoxExecutor(item, config=self.config) outcome_repr = ex.execute() outcome = ReprOutcome(outcome_repr) @@ -74,7 +74,7 @@ assert outcome.stdout.find("samfing elz") != -1 def test_cont_executor(self): - item = self.rootcol._getitembynames(self.funcprintfail_spec) + item = self.getexample("printfail") ex = AsyncExecutor(item, config=self.config) cont, pid = ex.execute() assert pid Modified: py/trunk/py/test/rsession/testing/test_reporter.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_reporter.py (original) +++ py/trunk/py/test/rsession/testing/test_reporter.py Tue Feb 6 21:36:03 2007 @@ -25,7 +25,7 @@ from py.__.test.rsession.outcome import ReprOutcome, Outcome from py.__.test.rsession.hostmanage import HostInfo from py.__.test.rsession.box import Box -from py.__.test.rsession.testing.runtest import BasicRsessionTest +from py.__.test.rsession.testing.basetest import BasicRsessionTest import sys from StringIO import StringIO @@ -57,7 +57,7 @@ return outcomes def report_received_item_outcome(self): - item = self.rootcol._getitembynames(self.funcpass_spec) + item = self.getexample("pass") outcomes = self.prepare_outcomes() def boxfun(config, item, outcomes): @@ -74,8 +74,8 @@ return out def _test_module(self): - funcitem = self.rootcol._getitembynames(self.funcpass_spec) - moditem = self.rootcol._getitembynames(self.mod_spec) + funcitem = self.getexample("pass") + moditem = self.getmod() outcomes = self.prepare_outcomes() def boxfun(config, item, funcitem, outcomes): Modified: py/trunk/py/test/rsession/testing/test_rsession.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_rsession.py (original) +++ py/trunk/py/test/rsession/testing/test_rsession.py Tue Feb 6 21:36:03 2007 @@ -6,7 +6,7 @@ from py.__.test.rsession import repevent from py.__.test.rsession.rsession import RSession from py.__.test.rsession.hostmanage import HostManager, HostInfo -from py.__.test.rsession.testing.runtest import BasicRsessionTest +from py.__.test.rsession.testing.basetest import BasicRsessionTest from py.__.test.rsession.testing.test_hostmanage import DirSetup def setup_module(mod): @@ -148,10 +148,10 @@ from py.__.test.rsession.testing.test_executor \ import ItemTestPassing, ItemTestFailing, ItemTestSkipping - itempass = self.rootcol._getitembynames(self.funcpass_spec) - itemfail = self.rootcol._getitembynames(self.funcfail_spec) - itemskip = self.rootcol._getitembynames(self.funcskip_spec) - itemprint = self.rootcol._getitembynames(self.funcprint_spec) + itempass = self.getexample("pass") + itemfail = self.getexample("fail") + itemskip = self.getexample("skip") + itemprint = self.getexample("print") # actually run some tests for node in nodes: From hpk at codespeak.net Tue Feb 6 21:47:23 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 6 Feb 2007 21:47:23 +0100 (CET) Subject: [py-svn] r38034 - py/trunk/py/test/rsession Message-ID: <20070206204723.096D510070@code0.codespeak.net> Author: hpk Date: Tue Feb 6 21:47:21 2007 New Revision: 38034 Modified: py/trunk/py/test/rsession/reporter.py Log: i think the rsync roots should be shown absolute Modified: py/trunk/py/test/rsession/reporter.py ============================================================================== --- py/trunk/py/test/rsession/reporter.py (original) +++ py/trunk/py/test/rsession/reporter.py Tue Feb 6 21:47:21 2007 @@ -85,10 +85,9 @@ self.out.sep("=", txt) self.timestart = item.timestart self.out.write("local top directory: %s\n" % item.topdir) - roots = [str(i.relto(item.topdir)) for i in item.roots] - for i, root in py.builtin.enumerate(roots): - outof = "%d/%d" %(i+1, len(roots)) - self.out.write("local RSync root [%s] %s\n" % + for i, root in py.builtin.enumerate(item.roots): + outof = "%d/%d" %(i+1, len(item.roots)) + self.out.write("local RSync root [%s]: %s\n" % (outof, root)) def report_RsyncFinished(self, item): From hpk at codespeak.net Tue Feb 6 22:12:40 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 6 Feb 2007 22:12:40 +0100 (CET) Subject: [py-svn] r38035 - in py/trunk/py/test/rsession: . testing Message-ID: <20070206211240.095FB10072@code0.codespeak.net> Author: hpk Date: Tue Feb 6 22:12:36 2007 New Revision: 38035 Added: py/trunk/py/test/rsession/testing/test_repevent.py - copied unchanged from r38034, py/trunk/py/test/rsession/testing/test_report.py Removed: py/trunk/py/test/rsession/testing/test_report.py Modified: py/trunk/py/test/rsession/hostmanage.py py/trunk/py/test/rsession/reporter.py py/trunk/py/test/rsession/testing/test_reporter.py Log: be more precise when reporting about hosts (enumerate them) Modified: py/trunk/py/test/rsession/hostmanage.py ============================================================================== --- py/trunk/py/test/rsession/hostmanage.py (original) +++ py/trunk/py/test/rsession/hostmanage.py Tue Feb 6 22:12:36 2007 @@ -25,7 +25,7 @@ def _getuniqueid(self, hostname): l = self._hostname2list.setdefault(hostname, []) - hostid = hostname + "_" * len(l) + hostid = hostname + "[%d]" % len(l) l.append(hostid) return hostid Modified: py/trunk/py/test/rsession/reporter.py ============================================================================== --- py/trunk/py/test/rsession/reporter.py (original) +++ py/trunk/py/test/rsession/reporter.py Tue Feb 6 22:12:36 2007 @@ -57,13 +57,15 @@ address) def report_HostRSyncing(self, item): - print "%10s: RSYNC ==> %s" % (item.host.hostname[:10], + hostrepr = self._hostrepr(item.host) + print "%15s: RSYNC ==> %s" % (hostrepr, item.host.relpath) def report_HostGatewayReady(self, item): self.to_rsync[item.host] = len(item.roots) - self.out.write("%10s: gateway initialised (remote topdir: %s)\n"\ - % (item.host.hostname, item.host.gw_remotepath)) + hostrepr = self._hostrepr(item.host) + self.out.write("%15s: gateway initialised (remote topdir: %s)\n"\ + % (hostrepr, item.host.gw_remotepath)) def report_HostRSyncRootReady(self, item): self.to_rsync[item.host] -= 1 @@ -72,15 +74,16 @@ def _host_ready(self, item): self.hosts_to_rsync -= 1 + hostrepr = self._hostrepr(item.host) if self.hosts_to_rsync: - print "%10s: READY (still %d to go)" % (item.host.hostname[:10], - self.hosts_to_rsync) + print "%15s: READY (still %d to go)" % (hostrepr, + self.hosts_to_rsync) else: - print "%10s: READY" % item.host.hostname[:10] + print "%15s: READY" % hostrepr def report_TestStarted(self, item): - hostnames = [host.hostname for host in item.hosts] - txt = " Test started, hosts: %s " % ", ".join(hostnames) + hostreprs = [self._hostrepr(host) for host in item.hosts] + txt = " Test started, hosts: %s " % ", ".join(hostreprs) self.hosts_to_rsync = len(item.hosts) self.out.sep("=", txt) self.timestart = item.timestart @@ -163,6 +166,9 @@ signal = outcome.signal self.out.line("Received signal: %d" % outcome.signal) self.repr_out_err(outcome) + + def _hostrepr(self, host): + return host.hostid def skips(self): texts = {} @@ -223,17 +229,18 @@ def report_ReceivedItemOutcome(self, event): host = event.host + hostrepr = self._hostrepr(host) if event.outcome.passed: self.passed[host] += 1 - sys.stdout.write("%10s: PASSED " % host.hostname[:10]) + sys.stdout.write("%15s: PASSED " % hostrepr) elif event.outcome.skipped: self.skipped_tests_outcome.append(event) self.skipped[host] += 1 - sys.stdout.write("%10s: SKIPPED " % host.hostname[:10]) + sys.stdout.write("%15s: SKIPPED " % hostrepr) else: self.failed[host] += 1 self.failed_tests_outcome.append(event) - sys.stdout.write("%10s: " % host.hostname[:10]) + sys.stdout.write("%15s: " % hostrepr) ansi_print("FAILED", esc=(31,1), newline=False, file=sys.stdout) sys.stdout.write(" ") # we should have printed 20 characters to this point Deleted: /py/trunk/py/test/rsession/testing/test_report.py ============================================================================== --- /py/trunk/py/test/rsession/testing/test_report.py Tue Feb 6 22:12:36 2007 +++ (empty file) @@ -1,36 +0,0 @@ -""" test reporting functionality. """ - -import py -from py.__.test.rsession import repevent - -def test_wrapcall_ok(): - l = [] - def ok(x): - return x+1 - i = repevent.wrapcall(l.append, ok, 1) - assert i == 2 - assert len(l) == 2 - assert isinstance(l[0], repevent.CallStart) - assert isinstance(l[1], repevent.CallFinish) - assert repr(l[0]) - assert repr(l[1]) - -def test_wrapcall_exception(): - l = [] - def fail(x): - raise ValueError - py.test.raises(ValueError, "repevent.wrapcall(l.append, fail, 1)") - assert len(l) == 2 - assert isinstance(l[0], repevent.CallStart) - assert isinstance(l[1], repevent.CallException) - -def test_reporter_methods_sanity(): - """ Checks if all the methods of reporter are sane - """ - from py.__.test.rsession.rsession import RemoteReporter - from py.__.test.rsession import repevent - - for method in dir(RemoteReporter): - - if method.startswith("report_") and method != "report_unknown": - assert method[len('report_'):] in repevent.__dict__ Modified: py/trunk/py/test/rsession/testing/test_reporter.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_reporter.py (original) +++ py/trunk/py/test/rsession/testing/test_reporter.py Tue Feb 6 22:12:36 2007 @@ -157,12 +157,15 @@ r.report(repevent.HostRSyncRootReady(host, root)) out, err = cap.reset() assert not err - expected1 = "Test started, hosts: host1, host2, host3" - expected2 = """host1: READY (still 2 to go) - host2: READY (still 1 to go) - host3: READY""" + expected1 = "Test started, hosts: host1[0], host2[0], host3[0]" assert out.find(expected1) != -1 - assert out.find(expected2) != -1 + for expected in py.code.Source(""" + host1[0]: READY (still 2 to go) + host2[0]: READY (still 1 to go) + host3[0]: READY + """).lines: + expected = expected.strip() + assert out.find(expected) != -1 class TestLocalReporter(AbstractTestReporter): reporter = LocalReporter From guido at codespeak.net Tue Feb 6 22:18:58 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Tue, 6 Feb 2007 22:18:58 +0100 (CET) Subject: [py-svn] r38036 - in py/trunk/py/apigen: . testing Message-ID: <20070206211858.2521D1006E@code0.codespeak.net> Author: guido Date: Tue Feb 6 22:18:56 2007 New Revision: 38036 Modified: py/trunk/py/apigen/html.py py/trunk/py/apigen/htmlgen.py py/trunk/py/apigen/testing/test_apigen_example.py Log: Some small code cleanups (moved more HTML generation code to html.py). Modified: py/trunk/py/apigen/html.py ============================================================================== --- py/trunk/py/apigen/html.py (original) +++ py/trunk/py/apigen/html.py Tue Feb 6 22:18:56 2007 @@ -23,7 +23,25 @@ pass class ClassDef(html.h1): - pass + def __init__(self, classname, bases, docstring, sourcelink, + properties, methods): + super(H.ClassDef, self).__init__('class %s(' % classname,) + for name, href in bases: + link = name + if href is not None: + link = H.a(name, href=href) + self.append(H.BaseDescription(link)) + self.append('):') + self.append(H.Docstring(docstring or '*no docstring available*')) + self.append(sourcelink) + if properties: + self.append(H.h2('properties:')) + for name, val in properties: + self.append(H.PropertyDescription(name, val)) + if methods: + self.append(H.h2('methods:')) + for methodhtml in methods: + self.append(methodhtml) class MethodDescription(Description): pass Modified: py/trunk/py/apigen/htmlgen.py ============================================================================== --- py/trunk/py/apigen/htmlgen.py (original) +++ py/trunk/py/apigen/htmlgen.py Tue Feb 6 22:18:56 2007 @@ -355,15 +355,7 @@ try: sourcefile = inspect.getsourcefile(cls) except TypeError: - sourcelink = 'builtin file, no source available' - else: - if sourcefile is None: - sourcelink = H.div('no source available') - else: - if sourcefile[-1] in ['o', 'c']: - sourcefile = sourcefile[:-1] - sourcelink = H.div(H.a('view source', - href=self.linker.get_lazyhref(sourcefile))) + sourcefile = None docstring = cls.__doc__ if docstring: @@ -375,32 +367,35 @@ bases = self.build_bases(dotted_name) properties = self.build_properties(cls) methods = self.build_methods(dotted_name) + + if sourcefile is None: + sourcelink = H.div('no source available') + else: + if sourcefile[-1] in ['o', 'c']: + sourcefile = sourcefile[:-1] + sourcelink = H.div(H.a('view source', + href=self.linker.get_lazyhref(sourcefile))) + snippet = H.ClassDescription( # XXX bases HTML - H.ClassDef('%s(' % (clsname,), *bases), - H.Docstring(docstring or '*no docstring available*'), - sourcelink, - *(properties+methods) + H.ClassDef(clsname, bases, docstring, sourcelink, + properties, methods), ) return snippet def build_bases(self, dotted_name): - basehtml = [] + ret = [] bases = self.dsa.get_possible_base_classes(dotted_name) for base in bases: try: obj = self.dsa.get_obj(base.name) except KeyError: - basehtml.append(base.name) + ret.append((base.name, None)) else: href = self.linker.get_lazyhref(base.name) - basehtml.append(H.BaseDescription(base.name, href=href)) - basehtml.append(',') - if basehtml: - basehtml.pop() - basehtml.append('):') - return basehtml + ret.append((base.name, href)) + return ret def build_properties(self, cls): properties = [] @@ -411,24 +406,17 @@ val = '' properties.append((attr, val)) properties.sort(key=lambda a: a[0]) # sort on name - ret = [] - if properties: - ret.append(H.h2('properties:')) - for name, val in properties: - ret.append(H.PropertyDescription(name, val)) - return ret + return properties def build_methods(self, dotted_name): ret = [] methods = self.dsa.get_class_methods(dotted_name) - if methods: - ret.append(H.h2('methods:')) - if '__init__' in methods: - methods.remove('__init__') - methods.insert(0, '__init__') - for method in methods: - ret += self.build_callable_view('%s.%s' % (dotted_name, - method)) + if '__init__' in methods: + methods.remove('__init__') + methods.insert(0, '__init__') + for method in methods: + ret += self.build_callable_view('%s.%s' % (dotted_name, + method)) return ret def build_namespace_view(self, namespace_dotted_name, item_dotted_names): @@ -455,6 +443,8 @@ def prepare_class_pages(self, classes_dotted_names): passed = [] for dotted_name in sorted(classes_dotted_names): + #if self.capture: + # self.capture.err.writeorg('preparing: %s\n' % (dotted_name,)) parent_dotted_name, _ = split_of_last_part(dotted_name) try: sibling_dotted_names = self.namespace_tree[parent_dotted_name] @@ -471,6 +461,8 @@ def build_class_pages(self, data, project): """ build the full api pages for a set of classes """ for dotted_name, tag, nav, reltargetpath in data: + #if self.capture: + # self.capture.err.writeorg('building: %s\n' % (dotted_name,)) title = 'api documentation for %s' % (dotted_name,) self.write_page(title, reltargetpath, project, tag, nav) @@ -497,6 +489,8 @@ def prepare_function_pages(self, method_dotted_names): passed = [] for dotted_name in sorted(method_dotted_names): + #if self.capture: + # self.capture.err.writeorg('preparing: %s\n' % (dotted_name,)) # XXX should we create a build_function_view instead? parent_dotted_name, _ = split_of_last_part(dotted_name) sibling_dotted_names = self.namespace_tree[parent_dotted_name] @@ -509,6 +503,8 @@ def build_function_pages(self, data, project): for dotted_name, tag, nav, reltargetpath in data: + #if self.capture: + # self.capture.err.writeorg('building: %s\n' % (dotted_name,)) title = 'api documentation for %s' % (dotted_name,) self.write_page(title, reltargetpath, project, tag, nav) @@ -521,6 +517,8 @@ function_names = self.dsa.get_function_names() class_names = self.dsa.get_class_names() for dotted_name in sorted(names): + #if self.capture: + # self.capture.err.writeorg('preparing: %s\n' % (dotted_name,)) if dotted_name in function_names or dotted_name in class_names: continue subitem_dotted_names = self.namespace_tree[dotted_name] @@ -537,6 +535,8 @@ def build_namespace_pages(self, data, project): for dotted_name, tag, nav, reltargetpath in data: + #if self.capture: + # self.capture.err.writeorg('building: %s\n' % (dotted_name,)) if dotted_name == '': dotted_name = self.dsa.get_module_name().split('/')[-1] title = 'index of %s namespace' % (dotted_name,) Modified: py/trunk/py/apigen/testing/test_apigen_example.py ============================================================================== --- py/trunk/py/apigen/testing/test_apigen_example.py (original) +++ py/trunk/py/apigen/testing/test_apigen_example.py Tue Feb 6 22:18:56 2007 @@ -116,7 +116,7 @@ self.namespace_tree = namespace_tree self.apb = ApiPageBuilder(base, linker, self.dsa, self.fs_root.join(self.pkg_name), - namespace_tree, 'root docstring') + namespace_tree) self.spb = SourcePageBuilder(base, linker, self.fs_root.join(self.pkg_name)) From hpk at codespeak.net Tue Feb 6 22:22:51 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 6 Feb 2007 22:22:51 +0100 (CET) Subject: [py-svn] r38037 - py/trunk/py/test/rsession Message-ID: <20070206212251.279781006E@code0.codespeak.net> Author: hpk Date: Tue Feb 6 22:22:50 2007 New Revision: 38037 Modified: py/trunk/py/test/rsession/hostmanage.py Log: not showing rsync-items by default, shows that something is odd regarding the messages, i think (read the diff, it's a simple change, i guess we actually want to generate an Event also for the web reporter at some point, i think, but we could live with this for the merge IMO) Modified: py/trunk/py/test/rsession/hostmanage.py ============================================================================== --- py/trunk/py/test/rsession/hostmanage.py (original) +++ py/trunk/py/test/rsession/hostmanage.py Tue Feb 6 22:22:50 2007 @@ -73,7 +73,8 @@ if 'ignores' in kwargs: ignores = kwargs.pop('ignores') self._ignores = ignores or [] - super(HostRSync, self).__init__(delete=True) + kwargs['delete'] = True + super(HostRSync, self).__init__(**kwargs) def filter(self, path): path = py.path.local(path) @@ -129,7 +130,8 @@ ignores = self.config.getvalue_pathlist("dist_rsync_ignore") self.prepare_gateways(reporter) for root in self.roots: - rsync = HostRSync(ignores=ignores) + rsync = HostRSync(ignores=ignores, + verbose=self.config.option.verbose) destrelpath = root.relto(self.config.topdir) for host in self.hosts: def donecallback(): From hpk at codespeak.net Tue Feb 6 22:41:13 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 6 Feb 2007 22:41:13 +0100 (CET) Subject: [py-svn] r38038 - py/trunk/py/test/rsession Message-ID: <20070206214113.00D081006E@code0.codespeak.net> Author: hpk Date: Tue Feb 6 22:41:11 2007 New Revision: 38038 Modified: py/trunk/py/test/rsession/hostmanage.py py/trunk/py/test/rsession/repevent.py py/trunk/py/test/rsession/reporter.py Log: more precise rsyncing Modified: py/trunk/py/test/rsession/hostmanage.py ============================================================================== --- py/trunk/py/test/rsession/hostmanage.py (original) +++ py/trunk/py/test/rsession/hostmanage.py Tue Feb 6 22:41:11 2007 @@ -94,7 +94,6 @@ if finishedcallback: finishedcallback() return False - reporter(repevent.HostRSyncing(host)) self._synced[key] = True # the follow attributes are set from host.initgateway() gw = host.gw @@ -104,7 +103,7 @@ super(HostRSync, self).add_target(gw, remotepath, finishedcallback) - return True # added the target + return remotepath class HostManager(object): def __init__(self, config, hosts=None): @@ -136,8 +135,9 @@ for host in self.hosts: def donecallback(): reporter(repevent.HostRSyncRootReady(host, root)) - rsync.add_target_host(host, reporter, destrelpath, - finishedcallback=donecallback) + remotepath = rsync.add_target_host( + host, reporter, destrelpath, finishedcallback=donecallback) + reporter(repevent.HostRSyncing(host, root, remotepath)) rsync.send(root) def setup_hosts(self, reporter): Modified: py/trunk/py/test/rsession/repevent.py ============================================================================== --- py/trunk/py/test/rsession/repevent.py (original) +++ py/trunk/py/test/rsession/repevent.py Tue Feb 6 22:41:11 2007 @@ -74,8 +74,10 @@ pass class HostRSyncing(ReportEvent): - def __init__(self, host): + def __init__(self, host, root, remotepath): self.host = host + self.root = root + self.remotepath = remotepath class HostGatewayReady(ReportEvent): def __init__(self, host, roots): Modified: py/trunk/py/test/rsession/reporter.py ============================================================================== --- py/trunk/py/test/rsession/reporter.py (original) +++ py/trunk/py/test/rsession/reporter.py Tue Feb 6 22:41:11 2007 @@ -58,8 +58,13 @@ def report_HostRSyncing(self, item): hostrepr = self._hostrepr(item.host) - print "%15s: RSYNC ==> %s" % (hostrepr, - item.host.relpath) + if not item.remotepath: + print "%15s: skip duplicate rsync of %r" % ( + hostrepr, item.root.basename) + else: + print "%15s: rsync %r to remote %s" % (hostrepr, + item.root.basename, + item.remotepath) def report_HostGatewayReady(self, item): self.to_rsync[item.host] = len(item.roots) From fijal at codespeak.net Wed Feb 7 00:00:28 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Wed, 7 Feb 2007 00:00:28 +0100 (CET) Subject: [py-svn] r38039 - py/trunk/py/test/rsession/testing Message-ID: <20070206230028.D52901007C@code0.codespeak.net> Author: fijal Date: Wed Feb 7 00:00:24 2007 New Revision: 38039 Modified: py/trunk/py/test/rsession/testing/test_rest.py Log: Fix a test Modified: py/trunk/py/test/rsession/testing/test_rest.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_rest.py (original) +++ py/trunk/py/test/rsession/testing/test_rest.py Wed Feb 7 00:00:24 2007 @@ -52,7 +52,8 @@ 'localhost\n\n') def test_report_HostRSyncing(self): - event = repevent.HostRSyncing(HostInfo('localhost:/foo/bar')) + event = repevent.HostRSyncing(HostInfo('localhost:/foo/bar'), "a", + "b") reporter.report(event) assert stdout.getvalue() == ('::\n\n localhost: RSYNC ==> ' '/foo/bar\n\n') From fijal at codespeak.net Wed Feb 7 00:13:53 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Wed, 7 Feb 2007 00:13:53 +0100 (CET) Subject: [py-svn] r38040 - py/trunk/py/test/rsession Message-ID: <20070206231353.2762C1007D@code0.codespeak.net> Author: fijal Date: Wed Feb 7 00:13:50 2007 New Revision: 38040 Modified: py/trunk/py/test/rsession/hostmanage.py Log: My favorite lazy-lexical-scope-binding error. Still thinking how to make test for that. Basically - callbacks with lexical scope variables are usually (depends of coz) called with variables after the for loop has finished. Modified: py/trunk/py/test/rsession/hostmanage.py ============================================================================== --- py/trunk/py/test/rsession/hostmanage.py (original) +++ py/trunk/py/test/rsession/hostmanage.py Wed Feb 7 00:13:50 2007 @@ -133,10 +133,11 @@ verbose=self.config.option.verbose) destrelpath = root.relto(self.config.topdir) for host in self.hosts: - def donecallback(): + def donecallback(host, root): reporter(repevent.HostRSyncRootReady(host, root)) remotepath = rsync.add_target_host( - host, reporter, destrelpath, finishedcallback=donecallback) + host, reporter, destrelpath, finishedcallback= + lambda host=host, root=root: donecallback(host, root)) reporter(repevent.HostRSyncing(host, root, remotepath)) rsync.send(root) From guido at codespeak.net Wed Feb 7 01:24:25 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Wed, 7 Feb 2007 01:24:25 +0100 (CET) Subject: [py-svn] r38043 - in py/trunk/py/apigen: . testing Message-ID: <20070207002425.685C410080@code0.codespeak.net> Author: guido Date: Wed Feb 7 01:24:21 2007 New Revision: 38043 Modified: py/trunk/py/apigen/apigen.py py/trunk/py/apigen/html.py py/trunk/py/apigen/htmlgen.py py/trunk/py/apigen/linker.py py/trunk/py/apigen/style.css py/trunk/py/apigen/testing/test_apigen_example.py py/trunk/py/apigen/testing/test_linker.py Log: Changed the way the lazy linking is handled: instead of building the pages and replacing the links all in memory, pages are now written to disk with temporary hrefs, which are replaced afterwards. Modified: py/trunk/py/apigen/apigen.py ============================================================================== --- py/trunk/py/apigen/apigen.py (original) +++ py/trunk/py/apigen/apigen.py Wed Feb 7 01:24:21 2007 @@ -34,7 +34,7 @@ def build(pkgdir, dsa, capture): # create a linker (link database) for cross-linking - l = linker.Linker() + l = linker.TempLinker() # create a project.Project instance to contain the LayoutPage instances proj = project.Project() @@ -52,27 +52,26 @@ # and build it apb = htmlgen.ApiPageBuilder(targetdir, l, dsa, pkgdir, namespace_tree, - capture, LayoutPage) - spb = htmlgen.SourcePageBuilder(targetdir, l, pkgdir, capture, LayoutPage) - - capture.err.writeorg('preparing namespace pages\n') - ns_data = apb.prepare_namespace_pages() - capture.err.writeorg('preparing class pages\n') - class_names = dsa.get_class_names() - class_data = apb.prepare_class_pages(class_names) - capture.err.writeorg('preparing function pages\n') - function_names = dsa.get_function_names() - func_data = apb.prepare_function_pages(function_names) - capture.err.writeorg('preparing source pages\n') - source_data = spb.prepare_pages(pkgdir) + proj, capture, LayoutPage) + spb = htmlgen.SourcePageBuilder(targetdir, l, pkgdir, proj, capture, + LayoutPage) capture.err.writeorg('building namespace pages\n') - apb.build_namespace_pages(ns_data, proj) + apb.build_namespace_pages() + capture.err.writeorg('building class pages\n') - apb.build_class_pages(class_data, proj) + class_names = dsa.get_class_names() + apb.build_class_pages(class_names) + capture.err.writeorg('building function pages\n') - apb.build_function_pages(func_data, proj) + function_names = dsa.get_function_names() + apb.build_function_pages(function_names) + capture.err.writeorg('building source pages\n') - spb.build_pages(source_data, proj, pkgdir) + spb.build_pages(pkgdir) + + capture.err.writeorg('replacing temporary links\n') + l.replace_dirpath(targetdir) + capture.err.writeorg('done building documentation\n') Modified: py/trunk/py/apigen/html.py ============================================================================== --- py/trunk/py/apigen/html.py (original) +++ py/trunk/py/apigen/html.py Wed Feb 7 01:24:21 2007 @@ -128,6 +128,8 @@ super(H.SourceCode, self).__init__(lntable, ltable) def add_line(self, lineno, els): + if els == []: + els = [u'\xa0'] self.linenotbody.append(H.tr(H.td(lineno, class_='lineno'))) self.linetbody.append(H.tr(H.td(class_='code', *els))) Modified: py/trunk/py/apigen/htmlgen.py ============================================================================== --- py/trunk/py/apigen/htmlgen.py (original) +++ py/trunk/py/apigen/htmlgen.py Wed Feb 7 01:24:21 2007 @@ -155,23 +155,26 @@ class AbstractPageBuilder(object): pageclass = LayoutPage - def write_page(self, title, reltargetpath, project, tag, nav): + def write_page(self, title, reltargetpath, tag, nav): targetpath = self.base.join(reltargetpath) relbase= relpath('%s%s' % (targetpath.dirpath(), targetpath.sep), self.base.strpath + '/') - page = wrap_page(project, title, tag, nav, relbase, self.base, + page = wrap_page(self.project, title, tag, nav, relbase, self.base, self.pageclass) - content = self.linker.call_withbase(reltargetpath, page.unicode) + # we write the page with _temporary_ hrefs here, need to be replaced + # from the TempLinker later + content = page.unicode() targetpath.ensure() targetpath.write(content.encode("utf8")) class SourcePageBuilder(AbstractPageBuilder): """ builds the html for a source docs page """ - def __init__(self, base, linker, projroot, capture=None, + def __init__(self, base, linker, projroot, project, capture=None, pageclass=LayoutPage): self.base = base self.linker = linker self.projroot = projroot + self.project = project self.capture = capture self.pageclass = pageclass @@ -240,13 +243,11 @@ try: tag = H.NonPythonSource(unicode(fspath.read(), 'utf-8')) except UnicodeError: - # XXX we should fix non-ascii support here!! tag = H.NonPythonSource('no source available (binary file?)') nav = self.build_navigation(fspath) return tag, nav - def prepare_pages(self, base): - passed = [] + def build_pages(self, base): for fspath in [base] + list(base.visit()): if fspath.ext in ['.pyc', '.pyo']: continue @@ -264,38 +265,36 @@ reloutputpath = reloutputpath.replace(os.path.sep, '/') outputpath = self.base.join(reloutputpath) self.linker.set_link(str(fspath), reloutputpath) - passed.append((fspath, outputpath)) - return passed + self.build_page(fspath, outputpath, base) - def build_pages(self, data, project, base): + def build_page(self, fspath, outputpath, base): """ build syntax-colored source views """ - for fspath, outputpath in data: - if fspath.check(ext='.py'): - try: - tag, nav = self.build_python_page(fspath) - except (KeyboardInterrupt, SystemError): - raise - except: # XXX strange stuff going wrong at times... need to fix - raise - exc, e, tb = py.std.sys.exc_info() - print '%s - %s' % (exc, e) - print - print ''.join(py.std.traceback.format_tb(tb)) - print '-' * 79 - del tb - tag, nav = self.build_nonpython_page(fspath) - elif fspath.check(dir=True): - tag, nav = self.build_dir_page(fspath) - else: + if fspath.check(ext='.py'): + try: + tag, nav = self.build_python_page(fspath) + except (KeyboardInterrupt, SystemError): + raise + except: # XXX strange stuff going wrong at times... need to fix + raise + exc, e, tb = py.std.sys.exc_info() + print '%s - %s' % (exc, e) + print + print ''.join(py.std.traceback.format_tb(tb)) + print '-' * 79 + del tb tag, nav = self.build_nonpython_page(fspath) - title = 'sources for %s' % (fspath.basename,) - reltargetpath = outputpath.relto(self.base).replace(os.path.sep, - '/') - self.write_page(title, reltargetpath, project, tag, nav) + elif fspath.check(dir=True): + tag, nav = self.build_dir_page(fspath) + else: + tag, nav = self.build_nonpython_page(fspath) + title = 'sources for %s' % (fspath.basename,) + reltargetpath = outputpath.relto(self.base).replace(os.path.sep, + '/') + self.write_page(title, reltargetpath, tag, nav) class ApiPageBuilder(AbstractPageBuilder): """ builds the html for an api docs page """ - def __init__(self, base, linker, dsa, projroot, namespace_tree, + def __init__(self, base, linker, dsa, projroot, namespace_tree, project, capture=None, pageclass=LayoutPage): self.base = base self.linker = linker @@ -303,6 +302,7 @@ self.projroot = projroot self.projpath = py.path.local(projroot) self.namespace_tree = namespace_tree + self.project = project self.capture = capture self.pageclass = pageclass @@ -440,11 +440,9 @@ ) return snippet - def prepare_class_pages(self, classes_dotted_names): + def build_class_pages(self, classes_dotted_names): passed = [] for dotted_name in sorted(classes_dotted_names): - #if self.capture: - # self.capture.err.writeorg('preparing: %s\n' % (dotted_name,)) parent_dotted_name, _ = split_of_last_part(dotted_name) try: sibling_dotted_names = self.namespace_tree[parent_dotted_name] @@ -455,42 +453,13 @@ nav = self.build_navigation(dotted_name, False) reltargetpath = "api/%s.html" % (dotted_name,) self.linker.set_link(dotted_name, reltargetpath) - passed.append((dotted_name, tag, nav, reltargetpath)) - return passed - - def build_class_pages(self, data, project): - """ build the full api pages for a set of classes """ - for dotted_name, tag, nav, reltargetpath in data: - #if self.capture: - # self.capture.err.writeorg('building: %s\n' % (dotted_name,)) title = 'api documentation for %s' % (dotted_name,) - self.write_page(title, reltargetpath, project, tag, nav) - - def prepare_method_pages(self, method_dotted_names): - # XXX note that even though these pages are still built, there's no nav - # pointing to them anymore... - passed = [] - for dotted_name in sorted(method_dotted_names): - parent_dotted_name, _ = split_of_last_part(dotted_name) - module_dotted_name, _ = split_of_last_part(parent_dotted_name) - sibling_dotted_names = self.namespace_tree[module_dotted_name] - tag = self.build_callable_view(dotted_name) - nav = self.build_navigation(dotted_name, False) - reltargetpath = "api/%s.html" % (dotted_name,) - self.linker.set_link(dotted_name, reltargetpath) - passed.append((dotted_name, tag, nav, reltargetpath)) + self.write_page(title, reltargetpath, tag, nav) return passed - - def build_method_pages(self, data, project): - for dotted_name, tag, nav, reltargetpath in data: - title = 'api documentation for %s' % (dotted_name,) - self.write_page(title, reltargetpath, project, tag, nav) - - def prepare_function_pages(self, method_dotted_names): + + def build_function_pages(self, method_dotted_names): passed = [] for dotted_name in sorted(method_dotted_names): - #if self.capture: - # self.capture.err.writeorg('preparing: %s\n' % (dotted_name,)) # XXX should we create a build_function_view instead? parent_dotted_name, _ = split_of_last_part(dotted_name) sibling_dotted_names = self.namespace_tree[parent_dotted_name] @@ -498,17 +467,11 @@ nav = self.build_navigation(dotted_name, False) reltargetpath = "api/%s.html" % (dotted_name,) self.linker.set_link(dotted_name, reltargetpath) - passed.append((dotted_name, tag, nav, reltargetpath)) - return passed - - def build_function_pages(self, data, project): - for dotted_name, tag, nav, reltargetpath in data: - #if self.capture: - # self.capture.err.writeorg('building: %s\n' % (dotted_name,)) title = 'api documentation for %s' % (dotted_name,) - self.write_page(title, reltargetpath, project, tag, nav) + self.write_page(title, reltargetpath, tag, nav) + return passed - def prepare_namespace_pages(self): + def build_namespace_pages(self): passed = [] module_name = self.dsa.get_module_name().split('/')[-1] @@ -517,8 +480,6 @@ function_names = self.dsa.get_function_names() class_names = self.dsa.get_class_names() for dotted_name in sorted(names): - #if self.capture: - # self.capture.err.writeorg('preparing: %s\n' % (dotted_name,)) if dotted_name in function_names or dotted_name in class_names: continue subitem_dotted_names = self.namespace_tree[dotted_name] @@ -530,17 +491,11 @@ else: reltargetpath = 'api/%s.html' % (dotted_name,) self.linker.set_link(dotted_name, reltargetpath) - passed.append((dotted_name, tag, nav, reltargetpath)) - return passed - - def build_namespace_pages(self, data, project): - for dotted_name, tag, nav, reltargetpath in data: - #if self.capture: - # self.capture.err.writeorg('building: %s\n' % (dotted_name,)) if dotted_name == '': dotted_name = self.dsa.get_module_name().split('/')[-1] title = 'index of %s namespace' % (dotted_name,) - self.write_page(title, reltargetpath, project, tag, nav) + self.write_page(title, reltargetpath, tag, nav) + return passed def build_navigation(self, dotted_name, build_children=True): navitems = [] Modified: py/trunk/py/apigen/linker.py ============================================================================== --- py/trunk/py/apigen/linker.py (original) +++ py/trunk/py/apigen/linker.py Wed Feb 7 01:24:21 2007 @@ -42,6 +42,57 @@ finally: del self.fromlocation +class TempLinker(object): + """ performs a similar role to the Linker, but with a different approach + + instead of returning 'lazy' hrefs, this returns a simple URL-style + string + + the 'temporary urls' are replaced on the filesystem after building the + files, so that means even though a second pass is still required, + things don't have to be built in-memory (as with the Linker) + """ + fromlocation = None + + def __init__(self): + self._linkid2target = {} + + def get_lazyhref(self, linkid): + return 'apigen.linker://%s' % (linkid,) + + def set_link(self, linkid, target): + assert linkid not in self._linkid2target + self._linkid2target[linkid] = target + + def get_target(self, tempurl, fromlocation=None): + linkid = '://'.join(tempurl.split('://')[1:]) + linktarget = self._linkid2target[linkid] + if fromlocation is not None: + linktarget = relpath(fromlocation, linktarget) + return linktarget + + _reg_tempurl = py.std.re.compile('"(apigen.linker:\/\/[^"\s]*)"') + def replace_dirpath(self, dirpath, stoponerrors=True): + """ replace temporary links in all html files in dirpath and below """ + for fpath in dirpath.visit('*.html'): + html = fpath.read() + while 1: + match = self._reg_tempurl.search(html) + if not match: + break + tempurl = match.group(1) + try: + html = html.replace('"' + tempurl + '"', + '"' + self.get_target(tempurl, + fpath.relto(dirpath)) + '"') + except KeyError: + if stoponerrors: + raise + html = html.replace('"' + tempurl + '"', + '"apigen.notfound://%s"' % (tempurl,)) + fpath.write(html) + + def relpath(p1, p2, sep=os.path.sep, back='..', normalize=True): """ create a relative path from p1 to p2 Modified: py/trunk/py/apigen/style.css ============================================================================== --- py/trunk/py/apigen/style.css (original) +++ py/trunk/py/apigen/style.css Wed Feb 7 01:24:21 2007 @@ -89,3 +89,10 @@ margin-bottom: 1em; } +td.lineno { + line-height: 1.1em; +} + +td.code { + line-height: 1.1em; +} Modified: py/trunk/py/apigen/testing/test_apigen_example.py ============================================================================== --- py/trunk/py/apigen/testing/test_apigen_example.py (original) +++ py/trunk/py/apigen/testing/test_apigen_example.py Wed Feb 7 01:24:21 2007 @@ -1,6 +1,7 @@ +# -*- coding: UTF-8 -*- import py html = py.xml.html -from py.__.apigen.linker import Linker +from py.__.apigen.linker import TempLinker from py.__.apigen.htmlgen import * from py.__.apigen.tracer.docstorage import DocStorage, DocStorageAccessor from py.__.apigen.tracer.tracer import Tracer @@ -96,15 +97,9 @@ cls.project = Project() def setup_method(self, meth): - class LinkerForTests(Linker): - def get_target(self, linkid): - try: - return super(LinkerForTests, self).get_target(linkid) - except KeyError: - return 'unknown_link_%s' % (linkid,) self.base = base = py.test.ensuretemp('%s_%s' % ( self.__class__.__name__, meth.im_func.func_name)) - self.linker = linker = LinkerForTests() + self.linker = linker = TempLinker() namespace_tree = create_namespace_tree(['main.sub', 'main.sub.func', 'main.SomeClass', @@ -116,9 +111,10 @@ self.namespace_tree = namespace_tree self.apb = ApiPageBuilder(base, linker, self.dsa, self.fs_root.join(self.pkg_name), - namespace_tree) + namespace_tree, self.project) self.spb = SourcePageBuilder(base, linker, - self.fs_root.join(self.pkg_name)) + self.fs_root.join(self.pkg_name), + self.project) class TestApiPageBuilder(AbstractBuilderTest): def test_build_callable_view(self): @@ -156,8 +152,7 @@ _checkhtmlsnippet(html) def test_build_function_pages(self): - data = self.apb.prepare_function_pages(['main.sub.func']) - self.apb.build_function_pages(data, self.project) + self.apb.build_function_pages(['main.sub.func']) funcfile = self.base.join('api/main.sub.func.html') assert funcfile.check() html = funcfile.read() @@ -169,19 +164,16 @@ _checkhtmlsnippet(html) def test_build_class_pages(self): - data = self.apb.prepare_class_pages(['main.SomeClass', - 'main.SomeSubClass']) - self.apb.build_class_pages(data, self.project) + self.apb.build_class_pages(['main.SomeClass', 'main.SomeSubClass']) clsfile = self.base.join('api/main.SomeClass.html') assert clsfile.check() html = clsfile.read() _checkhtml(html) def test_build_class_pages_instance(self): - data = self.apb.prepare_class_pages(['main.SomeClass', - 'main.SomeSubClass', - 'main.SomeInstance']) - self.apb.build_class_pages(data, self.project) + self.apb.build_class_pages(['main.SomeClass', + 'main.SomeSubClass', + 'main.SomeInstance']) clsfile = self.base.join('api/main.SomeInstance.html') assert clsfile.check() html = clsfile.read() @@ -191,13 +183,11 @@ ]) def test_build_class_pages_nav_links(self): - data = self.apb.prepare_class_pages(['main.SomeSubClass', - 'main.SomeClass']) - self.apb.prepare_namespace_pages() + self.apb.build_class_pages(['main.SomeSubClass', + 'main.SomeClass']) + self.apb.build_namespace_pages() # fake some stuff that would be built from other methods - self.linker.set_link('', 'api/index.html') - self.linker.set_link('main', 'api/main.html') - self.apb.build_class_pages(data, self.project) + self.linker.replace_dirpath(self.base, False) clsfile = self.base.join('api/main.SomeClass.html') assert clsfile.check() html = clsfile.read() @@ -217,9 +207,9 @@ _checkhtml(html) def test_build_class_pages_base_link(self): - data = self.apb.prepare_class_pages(['main.SomeSubClass', - 'main.SomeClass']) - self.apb.build_class_pages(data, self.project) + self.apb.build_class_pages(['main.SomeSubClass', + 'main.SomeClass']) + self.linker.replace_dirpath(self.base, False) clsfile = self.base.join('api/main.SomeSubClass.html') assert clsfile.check() html = clsfile.read() @@ -231,18 +221,15 @@ _checkhtml(html) def test_source_links(self): - data = self.apb.prepare_class_pages(['main.SomeSubClass', - 'main.SomeClass']) - sourcedata = self.spb.prepare_pages(self.fs_root) - self.apb.build_class_pages(data, self.project) - self.spb.build_pages(sourcedata, self.project, self.fs_root) + self.apb.build_class_pages(['main.SomeSubClass', 'main.SomeClass']) + self.spb.build_pages(self.fs_root) + self.linker.replace_dirpath(self.base, False) funchtml = self.base.join('api/main.SomeClass.html').read() assert funchtml.find('href="../source/pkg/someclass.py.html"') > -1 _checkhtml(funchtml) def test_build_namespace_pages(self): - data = self.apb.prepare_namespace_pages() - self.apb.build_namespace_pages(data, self.project) + self.apb.build_namespace_pages() mainfile = self.base.join('api/main.html') assert mainfile.check() html = mainfile.read() @@ -261,8 +248,7 @@ _checkhtml(otherhtml) def test_build_namespace_pages_index(self): - data = self.apb.prepare_namespace_pages() - self.apb.build_namespace_pages(data, self.project) + self.apb.build_namespace_pages() pkgfile = self.base.join('api/index.html') assert pkgfile.check() html = pkgfile.read() @@ -270,19 +256,18 @@ _checkhtml(html) def test_build_namespace_pages_subnamespace(self): - data = self.apb.prepare_namespace_pages() - self.apb.build_namespace_pages(data, self.project) + self.apb.build_namespace_pages() subfile = self.base.join('api/main.sub.html') assert subfile.check() html = subfile.read() _checkhtml(html) def test_build_function_api_pages_nav(self): - data = self.apb.prepare_function_pages(['main.sub.func']) + self.linker.set_link('main.sub', 'api/main.sub.html') self.linker.set_link('', 'api/index.html') self.linker.set_link('main', 'api/main.html') - self.linker.set_link('main.sub', 'api/main.sub.html') - self.apb.build_function_pages(data, self.project) + self.apb.build_function_pages(['main.sub.func']) + self.linker.replace_dirpath(self.base, False) funcfile = self.base.join('api/main.sub.func.html') html = funcfile.read() print html @@ -295,31 +280,32 @@ _checkhtml(html) def test_build_function_navigation(self): - self.apb.prepare_namespace_pages() - self.apb.prepare_function_pages(['main.sub.func']) - self.apb.prepare_class_pages(['main.SomeClass', - 'main.SomeSubClass', - 'main.SomeInstance']) - nav = self.apb.build_navigation('main.sub.func', False) - html = nav.unicode(indent=0) - print html.encode('UTF-8') - assert (u'' - u'
\xa0\xa0main
' - u'
\xa0\xa0\xa0\xa0' - u'SomeClass
' - u'
\xa0\xa0\xa0\xa0' - u'SomeInstance
' - u'
\xa0\xa0\xa0\xa0' - u'SomeSubClass
' - u'
\xa0\xa0\xa0\xa0' - u'sub
' - u'
\xa0\xa0\xa0\xa0\xa0\xa0' - u'func
' - ) in html + self.apb.build_namespace_pages() + self.apb.build_function_pages(['main.sub.func']) + self.apb.build_class_pages(['main.SomeClass', + 'main.SomeSubClass', + 'main.SomeInstance']) + self.linker.replace_dirpath(self.base, False) + html = self.base.join('api/main.sub.func.html').read() + print html + # XXX NOTE: do not mess with the string below, the spaces between the + #
and are actually UTF-8 \xa0 characters (non-breaking + # spaces)! + assert """\ + +""" in html def test_build_root_namespace_view(self): - data = self.apb.prepare_namespace_pages() - self.apb.build_namespace_pages(data, self.project) + self.apb.build_namespace_pages() + self.linker.replace_dirpath(self.base, False) rootfile = self.base.join('api/index.html') assert rootfile.check() html = rootfile.read() @@ -328,14 +314,13 @@ class TestSourcePageBuilder(AbstractBuilderTest): def test_build_pages(self): - data = self.spb.prepare_pages(self.fs_root) - self.spb.build_pages(data, self.project, self.fs_root) + self.spb.build_pages(self.fs_root) somesource = self.base.join('source/pkg/func.py.html').read() _checkhtml(somesource) def test_build_pages_nav(self): - data = self.spb.prepare_pages(self.fs_root) - self.spb.build_pages(data, self.project, self.fs_root) + self.spb.build_pages(self.fs_root) + self.linker.replace_dirpath(self.base, False) funcsource = self.base.join('source/pkg/func.py.html') assert funcsource.check(file=True) html = funcsource.read() @@ -348,8 +333,8 @@ ]) def test_build_dir_page(self): - data = self.spb.prepare_pages(self.fs_root) - self.spb.build_pages(data, self.project, self.fs_root) + self.spb.build_pages(self.fs_root) + self.linker.replace_dirpath(self.base, False) pkgindex = self.base.join('source/pkg/index.html') assert pkgindex.check(file=True) html = pkgindex.read() @@ -365,8 +350,8 @@ _checkhtml(html) def test_build_source_page(self): - data = self.spb.prepare_pages(self.fs_root) - self.spb.build_pages(data, self.project, self.fs_root) + self.spb.build_pages(self.fs_root) + self.linker.replace_dirpath(self.base, False) funcsource = self.base.join('source/pkg/func.py.html') assert funcsource.check(file=True) html = funcsource.read() @@ -374,14 +359,14 @@ assert ('def func(arg1)') in html def test_build_navigation_root(self): - self.spb.prepare_pages(self.fs_root) - nav = self.spb.build_navigation(self.fs_root.join('pkg')) - html = nav.unicode(indent=0) - print html.encode('UTF-8') + self.spb.build_pages(self.fs_root) + self.linker.replace_dirpath(self.base) + html = self.base.join('source/pkg/index.html').read() + print html run_string_sequence_test(html, [ - 'href="source/pkg/index.html">pkg', - 'href="source/pkg/func.py.html">func.py', - 'href="source/pkg/someclass.py.html">someclass.py', - 'href="source/pkg/somesubclass.py.html">somesubclass.py', + 'href="index.html">pkg', + 'href="func.py.html">func.py', + 'href="someclass.py.html">someclass.py', + 'href="somesubclass.py.html">somesubclass.py', ]) Modified: py/trunk/py/apigen/testing/test_linker.py ============================================================================== --- py/trunk/py/apigen/testing/test_linker.py (original) +++ py/trunk/py/apigen/testing/test_linker.py Wed Feb 7 01:24:21 2007 @@ -1,5 +1,5 @@ import py -from py.__.apigen.linker import Linker, getrelfspath, relpath +from py.__.apigen.linker import Linker, TempLinker, getrelfspath, relpath class TestLinker(object): def test_get_target(self): @@ -29,6 +29,24 @@ 'c:\\foo\\bar c:\\foo ../foo \\', ] +class TestTempLinker(object): + def test_get_target(self): + linker = TempLinker() + temphref = linker.get_lazyhref('py.path.local') + linker.set_link('py.path.local', 'py/path/local.html') + relpath = linker.get_target(temphref) + assert relpath == 'py/path/local.html' + + def test_functional(self): + temp = py.test.ensuretemp('TestTempLinker.test_functional') + l = TempLinker() + bar = temp.ensure('foo/bar.html', file=True) + baz = temp.ensure('foo/baz.html', file=True) + l.set_link(baz.strpath, baz.relto(temp)) + bar.write('baz' % (l.get_lazyhref(baz.strpath),)) + l.replace_dirpath(temp) + assert bar.read() == 'baz' + def gen_check(frompath, topath, sep, expected): result = relpath(frompath, topath, sep=sep) assert result == expected From guido at codespeak.net Wed Feb 7 02:01:29 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Wed, 7 Feb 2007 02:01:29 +0100 (CET) Subject: [py-svn] r38044 - py/trunk/py/apigen Message-ID: <20070207010129.AEA7410080@code0.codespeak.net> Author: guido Date: Wed Feb 7 02:01:25 2007 New Revision: 38044 Modified: py/trunk/py/apigen/apigen.py py/trunk/py/apigen/html.py py/trunk/py/apigen/htmlgen.py py/trunk/py/apigen/style.css Log: Now just writing dots when running tests, decreased font size a little of the class docstring and link. Modified: py/trunk/py/apigen/apigen.py ============================================================================== --- py/trunk/py/apigen/apigen.py (original) +++ py/trunk/py/apigen/apigen.py Wed Feb 7 02:01:25 2007 @@ -56,22 +56,11 @@ spb = htmlgen.SourcePageBuilder(targetdir, l, pkgdir, proj, capture, LayoutPage) - capture.err.writeorg('building namespace pages\n') apb.build_namespace_pages() - - capture.err.writeorg('building class pages\n') class_names = dsa.get_class_names() apb.build_class_pages(class_names) - - capture.err.writeorg('building function pages\n') function_names = dsa.get_function_names() apb.build_function_pages(function_names) - - capture.err.writeorg('building source pages\n') spb.build_pages(pkgdir) - - capture.err.writeorg('replacing temporary links\n') l.replace_dirpath(targetdir) - capture.err.writeorg('done building documentation\n') - Modified: py/trunk/py/apigen/html.py ============================================================================== --- py/trunk/py/apigen/html.py (original) +++ py/trunk/py/apigen/html.py Wed Feb 7 02:01:25 2007 @@ -32,8 +32,10 @@ link = H.a(name, href=href) self.append(H.BaseDescription(link)) self.append('):') - self.append(H.Docstring(docstring or '*no docstring available*')) - self.append(sourcelink) + self.append(H.div(H.Docstring(docstring or + '*no docstring available*'), + sourcelink, + class_='classdoc')) if properties: self.append(H.h2('properties:')) for name, val in properties: Modified: py/trunk/py/apigen/htmlgen.py ============================================================================== --- py/trunk/py/apigen/htmlgen.py (original) +++ py/trunk/py/apigen/htmlgen.py Wed Feb 7 02:01:25 2007 @@ -251,6 +251,8 @@ for fspath in [base] + list(base.visit()): if fspath.ext in ['.pyc', '.pyo']: continue + if self.capture: + self.capture.err.writeorg('.') relfspath = fspath.relto(base) if relfspath.find('%s.' % (os.path.sep,)) > -1: # skip hidden dirs and files @@ -443,6 +445,8 @@ def build_class_pages(self, classes_dotted_names): passed = [] for dotted_name in sorted(classes_dotted_names): + if self.capture: + self.capture.err.writeorg('.') parent_dotted_name, _ = split_of_last_part(dotted_name) try: sibling_dotted_names = self.namespace_tree[parent_dotted_name] @@ -460,6 +464,8 @@ def build_function_pages(self, method_dotted_names): passed = [] for dotted_name in sorted(method_dotted_names): + if self.capture: + self.capture.err.writeorg('.') # XXX should we create a build_function_view instead? parent_dotted_name, _ = split_of_last_part(dotted_name) sibling_dotted_names = self.namespace_tree[parent_dotted_name] @@ -480,6 +486,8 @@ function_names = self.dsa.get_function_names() class_names = self.dsa.get_class_names() for dotted_name in sorted(names): + if self.capture: + self.capture.err.writeorg('.') if dotted_name in function_names or dotted_name in class_names: continue subitem_dotted_names = self.namespace_tree[dotted_name] Modified: py/trunk/py/apigen/style.css ============================================================================== --- py/trunk/py/apigen/style.css (original) +++ py/trunk/py/apigen/style.css Wed Feb 7 02:01:25 2007 @@ -34,6 +34,10 @@ list-style-type: none; } +.classdoc { + font-size: 0.9em; +} + .code a { color: blue; font-weight: bold; From fijal at codespeak.net Wed Feb 7 12:34:12 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Wed, 7 Feb 2007 12:34:12 +0100 (CET) Subject: [py-svn] r38051 - py/trunk/py/test/rsession Message-ID: <20070207113412.3E25410072@code0.codespeak.net> Author: fijal Date: Wed Feb 7 12:34:08 2007 New Revision: 38051 Modified: py/trunk/py/test/rsession/rsession.py Log: Fix an option -s contradics -d Modified: py/trunk/py/test/rsession/rsession.py ============================================================================== --- py/trunk/py/test/rsession/rsession.py (original) +++ py/trunk/py/test/rsession/rsession.py Wed Feb 7 12:34:08 2007 @@ -29,6 +29,9 @@ if option.runbrowser and not option.startserver: #print "--runbrowser implies --startserver" option.startserver = True + if option.nocapture: + print "Cannot use nocapture with distributed testing" + sys.exit(1) super(AbstractSession, self).fixoptions() def init_reporter(self, reporter, hosts, reporter_class, arg=""): From guido at codespeak.net Wed Feb 7 16:15:03 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Wed, 7 Feb 2007 16:15:03 +0100 (CET) Subject: [py-svn] r38068 - py/trunk/py/path/svn Message-ID: <20070207151503.4837310083@code0.codespeak.net> Author: guido Date: Wed Feb 7 16:15:01 2007 New Revision: 38068 Modified: py/trunk/py/path/svn/wccommand.py Log: Grmbl, the failing svn tests turned out to be my fault: copy/paste bug. (when copying the comment, I must have copied along some lines of code... ): Modified: py/trunk/py/path/svn/wccommand.py ============================================================================== --- py/trunk/py/path/svn/wccommand.py (original) +++ py/trunk/py/path/svn/wccommand.py Wed Feb 7 16:15:01 2007 @@ -164,9 +164,6 @@ def mkdir(self, *args): """ create & return the directory joined with args. """ - p = self.join(*args) - self._callex(os.mkdir, str(p)) - return p if args: return self.join(*args).mkdir() else: From guido at codespeak.net Wed Feb 7 16:24:12 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Wed, 7 Feb 2007 16:24:12 +0100 (CET) Subject: [py-svn] r38070 - py/trunk/py/doc/talk Message-ID: <20070207152412.BBBCB10088@code0.codespeak.net> Author: guido Date: Wed Feb 7 16:24:11 2007 New Revision: 38070 Modified: py/trunk/py/doc/talk/pytest-overview.txt Log: Removed broken anchor from link. Modified: py/trunk/py/doc/talk/pytest-overview.txt ============================================================================== --- py/trunk/py/doc/talk/pytest-overview.txt (original) +++ py/trunk/py/doc/talk/pytest-overview.txt Wed Feb 7 16:24:11 2007 @@ -118,7 +118,7 @@ - uses py lib extensively (py.path/py.execnet) - "conftest.py" per-directory configuration mechanism -.. _`basic picture`: ../test.html#basicpicture +.. _`basic picture`: ../test.html Session objects =============================== From hpk at codespeak.net Wed Feb 7 16:26:31 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 7 Feb 2007 16:26:31 +0100 (CET) Subject: [py-svn] r38074 - py/trunk/py/path/testing Message-ID: <20070207152631.6441E10094@code0.codespeak.net> Author: hpk Date: Wed Feb 7 16:26:27 2007 New Revision: 38074 Modified: py/trunk/py/path/testing/common.py Log: fix typo Modified: py/trunk/py/path/testing/common.py ============================================================================== --- py/trunk/py/path/testing/common.py (original) +++ py/trunk/py/path/testing/common.py Wed Feb 7 16:26:27 2007 @@ -135,7 +135,7 @@ def test_listdir_fnmatchstring(self): l = self.root.listdir('s*dir') - assert len(l), 1 + assert len(l) assert l[0], self.root.join('sampledir') def test_listdir_filter(self): From guido at codespeak.net Wed Feb 7 16:33:48 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Wed, 7 Feb 2007 16:33:48 +0100 (CET) Subject: [py-svn] r38075 - py/trunk/py/test/rsession Message-ID: <20070207153348.E64F910087@code0.codespeak.net> Author: guido Date: Wed Feb 7 16:33:46 2007 New Revision: 38075 Modified: py/trunk/py/test/rsession/rsession.py Log: Adding some print. Modified: py/trunk/py/test/rsession/rsession.py ============================================================================== --- py/trunk/py/test/rsession/rsession.py (original) +++ py/trunk/py/test/rsession/rsession.py Wed Feb 7 16:33:46 2007 @@ -227,6 +227,7 @@ capture) finally: capture.reset() + print >>sys.stderr, '\ndone' def init_runner(self): if self.config.option.apigen: From guido at codespeak.net Wed Feb 7 16:34:03 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Wed, 7 Feb 2007 16:34:03 +0100 (CET) Subject: [py-svn] r38076 - py/trunk/py/execnet Message-ID: <20070207153403.4000C1008D@code0.codespeak.net> Author: guido Date: Wed Feb 7 16:34:01 2007 New Revision: 38076 Modified: py/trunk/py/execnet/rsync.py Log: Typo. Modified: py/trunk/py/execnet/rsync.py ============================================================================== --- py/trunk/py/execnet/rsync.py (original) +++ py/trunk/py/execnet/rsync.py Wed Feb 7 16:34:01 2007 @@ -96,7 +96,7 @@ """ Sends a sourcedir to all added targets. """ if not self._channels: - raise IOError("no targets available, maybing you " + raise IOError("no targets available, maybe you " "are trying call send() twice?") self._sourcedir = str(sourcedir) # normalize a trailing '/' away @@ -143,7 +143,6 @@ channel.send((str(destdir), self._options)) self._channels[channel] = finishedcallback - def _broadcast(self, msg): for channel in self._channels: channel.send(msg) From guido at codespeak.net Wed Feb 7 16:36:04 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Wed, 7 Feb 2007 16:36:04 +0100 (CET) Subject: [py-svn] r38077 - in py/trunk/py: bin misc/testing Message-ID: <20070207153604.32B4A1008B@code0.codespeak.net> Author: guido Date: Wed Feb 7 16:36:02 2007 New Revision: 38077 Added: py/trunk/py/bin/_update_website.py (contents, props changed) py/trunk/py/misc/testing/test_update_website.py Log: Script to run "py.test --apigen" on the py lib (well, can be used on other projects too) and rsync the results to some remote host/path (by default codespeak.net, currently to some dir in my home directory, when it's tested better in practice files will go to the website directory). Added: py/trunk/py/bin/_update_website.py ============================================================================== --- (empty file) +++ py/trunk/py/bin/_update_website.py Wed Feb 7 16:36:02 2007 @@ -0,0 +1,65 @@ +#!/usr/bin/env python + +""" run py.test with the --apigen option and rsync the results to a host + + rsyncs the whole package (with all the ReST docs converted to HTML) as well + as the apigen docs to a given remote host and path +""" + +import py +import sys + +def rsync(pkgpath, apidocspath, gateway, remotepath): + """ copy the code and docs to the remote host """ + # copy to a temp dir first, even though both paths (normally) share the + # same parent dir, that may contain other stuff that we don't want to + # copy... + tempdir = py.test.ensuretemp('update_website_rsync_temp') + pkgpath.copy(tempdir.ensure(pkgpath.basename, dir=True)) + apidocspath.copy(tempdir.ensure(apidocspath.basename, dir=True)) + + rs = py.execnet.RSync(delete=True) + rs.add_target(gateway, remotepath) + rs.send(tempdir) + +def run_tests(pkgpath, args=''): + """ run the unit tests and build the docs """ + pypath = py.__package__.getpath() + pytestpath = pypath.join('bin/py.test') + # XXX this would need a Windows specific version if we want to allow + # running this script on that platform, but currently --apigen doesn't + # work there anyway... + apigenpath = pkgpath.join('apigen/apigen.py') # XXX be more general here? + if not apigenpath.check(file=True): + apigenpath = pypath.join('apigen/apigen.py') + cmd = 'PYTHONPATH="%s:%s" "%s" --apigen="%s" "%s" %s' % (pypath.dirpath(), + pkgpath.dirpath(), + pytestpath, + apigenpath, + pkgpath, + args) + status = py.std.os.system(cmd) + return status + +def main(pkgpath, apidocspath, rhost, rpath, args=''): + print 'running tests' + errors = run_tests(pkgpath, args) + if errors: + print >>sys.stderr, \ + 'Errors while running the unit tests: %s' % (errors,) + sys.exit(1) + + print 'rsyncing' + gateway = py.execnet.SshGateway(rhost) + errors = rsync(pkgpath, apidocspath, gateway, rpath) + if errors: + print >>sys.stderr, 'Errors while rsyncing: %s' + sys.exit(1) + +if __name__ == '__main__': + args = ' '.join(sys.argv[1:]) + pkgpath = py.__package__.getpath() + apidocspath = pkgpath.dirpath().join('apigen') + main(pkgpath, apidocspath, 'codespeak.net', + '/home/guido/rsynctests', args) + Added: py/trunk/py/misc/testing/test_update_website.py ============================================================================== --- (empty file) +++ py/trunk/py/misc/testing/test_update_website.py Wed Feb 7 16:36:02 2007 @@ -0,0 +1,59 @@ +import py + +here = py.magic.autopath().dirpath() +update_website = here.join('../../bin/_update_website.py').pyimport() + +def test_rsync(): + temp = py.test.ensuretemp('update_website_rsync') + pkgpath = temp.join('pkg') + apipath = temp.join('apigen') + pkgpath.ensure('foo/bar.txt', file=True).write('baz') + pkgpath.ensure('spam/eggs.txt', file=True).write('spam') + apipath.ensure('api/foo.html', file=True).write('') + apipath.ensure('source/spam.html', file=True).write('') + + rsyncpath = temp.join('rsync') + assert not rsyncpath.check() + gateway = py.execnet.PopenGateway() + update_website.rsync(pkgpath, apipath, gateway, rsyncpath.strpath) + assert rsyncpath.check(dir=True) + assert rsyncpath.join('pkg').check(dir=True) + assert rsyncpath.join('pkg/spam/eggs.txt').read() == 'spam' + assert rsyncpath.join('apigen').check(dir=True) + assert rsyncpath.join('apigen/api/foo.html').read() == '' + +def setup_pkg(testname): + temp = py.test.ensuretemp(testname) + pkgpath = temp.ensure('pkg', dir=True) + pyfile = pkgpath.ensure('mod.py').write(py.code.Source(""" + def foo(x): + return x + 1 + """)) + testfile = pkgpath.ensure('test/test_mod.py').write(py.code.Source(""" + from pkg.sub import foo + def test_foo(): + assert foo(1) == 2 + """)) + initfile = pkgpath.ensure('__init__.py').write(py.code.Source("""\ + import py + from py.__.initpkg import initpkg + initpkg(__name__, exportdefs={ + 'sub.foo': ('./mod.py', 'foo'), + }) + """)) + return pkgpath + +def test_run_tests(): + pkgpath = setup_pkg('update_website_run_tests') + errors = update_website.run_tests(pkgpath) + assert not errors + assert pkgpath.join('../apigen').check(dir=True) + assert pkgpath.join('../apigen/api/sub.foo.html').check(file=True) + +def test_run_tests_failure(): + pkgpath = setup_pkg('update_website_run_tests_failure') + assert not pkgpath.join('../apigen').check(dir=True) + pkgpath.ensure('../apigen', file=True) + errors = update_website.run_tests(pkgpath) + assert errors # some error message + From hpk at codespeak.net Wed Feb 7 16:54:52 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 7 Feb 2007 16:54:52 +0100 (CET) Subject: [py-svn] r38078 - in py/trunk/py: misc/testing test/rsession test/rsession/testing Message-ID: <20070207155452.51E6010089@code0.codespeak.net> Author: hpk Date: Wed Feb 7 16:54:50 2007 New Revision: 38078 Modified: py/trunk/py/misc/testing/test_update_website.py py/trunk/py/test/rsession/hostmanage.py py/trunk/py/test/rsession/rsession.py py/trunk/py/test/rsession/testing/test_executor.py py/trunk/py/test/rsession/testing/test_rsession.py Log: some fixes and test skips for win32 (otherwise all tests pass for me on win32) Modified: py/trunk/py/misc/testing/test_update_website.py ============================================================================== --- py/trunk/py/misc/testing/test_update_website.py (original) +++ py/trunk/py/misc/testing/test_update_website.py Wed Feb 7 16:54:50 2007 @@ -44,6 +44,8 @@ return pkgpath def test_run_tests(): + if py.std.sys.platform == "win32": + py.test.skip("update_website is not supposed to be run from win32") pkgpath = setup_pkg('update_website_run_tests') errors = update_website.run_tests(pkgpath) assert not errors Modified: py/trunk/py/test/rsession/hostmanage.py ============================================================================== --- py/trunk/py/test/rsession/hostmanage.py (original) +++ py/trunk/py/test/rsession/hostmanage.py Wed Feb 7 16:54:50 2007 @@ -183,7 +183,7 @@ homedir = os.environ.get('HOME', '') if not homedir: homedir = os.environ.get('HOMEPATH', '.') - return homedir + return os.path.abspath(homedir) def getpath_relto_home(targetpath): import os Modified: py/trunk/py/test/rsession/rsession.py ============================================================================== --- py/trunk/py/test/rsession/rsession.py (original) +++ py/trunk/py/test/rsession/rsession.py Wed Feb 7 16:54:50 2007 @@ -17,8 +17,6 @@ from py.__.test.session import Session from py.__.test.outcome import Skipped, Failed -old_fork = os.fork - class AbstractSession(Session): """ An abstract session executes collectors/items through a runner. Modified: py/trunk/py/test/rsession/testing/test_executor.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_executor.py (original) +++ py/trunk/py/test/rsession/testing/test_executor.py Wed Feb 7 16:54:50 2007 @@ -7,9 +7,9 @@ from py.__.test.rsession.outcome import ReprOutcome from py.__.test.rsession.testing.basetest import BasicRsessionTest -#def setup_module(mod): -# mod.rootdir = py.path.local(py.__file__).dirpath().dirpath() -# mod.config = py.test.config._reparse([mod.rootdir]) +def setup_module(mod): + if py.std.sys.platform == "win32": + py.test.skip("skipping executor tests (some require os.fork)") class ItemTestPassing(py.test.Item): def run(self): Modified: py/trunk/py/test/rsession/testing/test_rsession.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_rsession.py (original) +++ py/trunk/py/test/rsession/testing/test_rsession.py Wed Feb 7 16:54:50 2007 @@ -11,6 +11,8 @@ def setup_module(mod): mod.pkgdir = py.path.local(py.__file__).dirpath() + if py.std.sys.platform == "win32": + py.test.skip("rsession tests disabled for win32") def test_example_tryiter(): events = [] From guido at codespeak.net Wed Feb 7 17:03:22 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Wed, 7 Feb 2007 17:03:22 +0100 (CET) Subject: [py-svn] r38079 - in py/trunk/py: apigen test Message-ID: <20070207160322.2BE9C10089@code0.codespeak.net> Author: guido Date: Wed Feb 7 17:03:19 2007 New Revision: 38079 Modified: py/trunk/py/apigen/style.css py/trunk/py/test/defaultconftest.py Log: Typo in some help string, and made fonts of apigen a bit smaller somewhere. Modified: py/trunk/py/apigen/style.css ============================================================================== --- py/trunk/py/apigen/style.css (original) +++ py/trunk/py/apigen/style.css Wed Feb 7 17:03:19 2007 @@ -35,7 +35,7 @@ } .classdoc { - font-size: 0.9em; + font-size: 0.8em; } .code a { Modified: py/trunk/py/test/defaultconftest.py ============================================================================== --- py/trunk/py/test/defaultconftest.py (original) +++ py/trunk/py/test/defaultconftest.py Wed Feb 7 17:03:19 2007 @@ -95,7 +95,7 @@ help="restructured text output reporting."), Option('', '--apigen', action="store", dest="apigen", - help="generate api documentation while testing (requires" + help="generate api documentation while testing (requires " "argument pointing to a script)."), Option('', '--session', action="store", dest="session", default=None, From fijal at codespeak.net Wed Feb 7 17:49:16 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Wed, 7 Feb 2007 17:49:16 +0100 (CET) Subject: [py-svn] r38082 - py/trunk/py/test/rsession/testing Message-ID: <20070207164916.7EAA41007B@code0.codespeak.net> Author: fijal Date: Wed Feb 7 17:49:14 2007 New Revision: 38082 Modified: py/trunk/py/test/rsession/testing/test_rsession.py Log: Fix reporting (and a test for that) Modified: py/trunk/py/test/rsession/testing/test_rsession.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_rsession.py (original) +++ py/trunk/py/test/rsession/testing/test_rsession.py Wed Feb 7 17:49:14 2007 @@ -115,7 +115,7 @@ assert tb[0].source.find("execute") != -1 def test_setup_teardown_ssh(self): - hosts = [HostInfo('localhost')] + hosts = [HostInfo('localhost:%s' % self.dest)] setup_events = [] teardown_events = [] tmpdir = py.test.ensuretemp("emptyconftest") @@ -141,7 +141,7 @@ assert len(teardown_wait_ends) == len(hosts) def test_setup_teardown_run_ssh(self): - hosts = [HostInfo('localhost')] + hosts = [HostInfo('localhost:%s' % self.dest)] allevents = [] hm = HostManager(self.config, hosts=hosts) @@ -181,13 +181,13 @@ """ Tests if nice level behaviour is ok """ allevents = [] - hosts = [HostInfo('localhost')] + hosts = [HostInfo('localhost:%s' % self.dest)] tmpdir = py.test.ensuretemp("nice") tmpdir.ensure("__init__.py") tmpdir.ensure("conftest.py").write(py.code.Source(""" - dist_hosts = ['localhost'] + dist_hosts = ['localhost:%s'] dist_nicelevel = 10 - """)) + """ % self.dest)) tmpdir.ensure("test_one.py").write("""def test_nice(): import os assert os.nice(0) == 10 From hpk at codespeak.net Wed Feb 7 18:58:49 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 7 Feb 2007 18:58:49 +0100 (CET) Subject: [py-svn] r38083 - py/trunk/py/test/testing Message-ID: <20070207175849.90C0F1007F@code0.codespeak.net> Author: hpk Date: Wed Feb 7 18:58:47 2007 New Revision: 38083 Modified: py/trunk/py/test/testing/test_collect.py Log: saner tempdir using Modified: py/trunk/py/test/testing/test_collect.py ============================================================================== --- py/trunk/py/test/testing/test_collect.py (original) +++ py/trunk/py/test/testing/test_collect.py Wed Feb 7 18:58:47 2007 @@ -79,11 +79,11 @@ py.test.raises(py.error.ENOENT, col.run) def test_syntax_error_in_module(): - (datadir / 'syntax_error.py').write("\nthis is really not python\n") + p = py.test.ensuretemp("syntaxerror1").join('syntax_error.py') + p.write("\nthis is really not python\n") modpath = datadir.join('syntax_error.py') col = py.test.collect.Module(modpath) py.test.raises(SyntaxError, col.run) - (datadir / 'syntax_error.py').remove() def test_disabled_class(): col = py.test.collect.Module(datadir.join('disabled.py')) From fijal at codespeak.net Wed Feb 7 19:25:03 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Wed, 7 Feb 2007 19:25:03 +0100 (CET) Subject: [py-svn] r38091 - py/trunk/py/test/rsession Message-ID: <20070207182503.A3CA310086@code0.codespeak.net> Author: fijal Date: Wed Feb 7 19:25:01 2007 New Revision: 38091 Modified: py/trunk/py/test/rsession/master.py Log: Increase verbosity in case of error. The thing is that we usually don't get remote error, so let's print it. Modified: py/trunk/py/test/rsession/master.py ============================================================================== --- py/trunk/py/test/rsession/master.py (original) +++ py/trunk/py/test/rsession/master.py Wed Feb 7 19:25:01 2007 @@ -23,14 +23,27 @@ self.channel, item, repr_outcome)) def send(self, item): - if item is StopIteration: - self.channel.send(42) - else: - self.pending.insert(0, item) + try: + if item is StopIteration: + self.channel.send(42) + else: + self.pending.insert(0, item) #itemspec = item.listnames()[1:] - self.channel.send(item._get_collector_trail()) - # send start report - self.reporter(repevent.SendItem(self.channel, item)) + self.channel.send(item._get_collector_trail()) + # send start report + self.reporter(repevent.SendItem(self.channel, item)) + except IOError: + + try: + channel._getremoterror() + except: + # if this were not remote, we've got no clue + excinfo = py.code.ExceptionInfo() + for i in excinfo.traceback: + print str(i)[2:-1] + print excinfo + else: + raise def itemgen(colitems, reporter, keyword, reporterror): def rep(x): From fijal at codespeak.net Wed Feb 7 19:37:07 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Wed, 7 Feb 2007 19:37:07 +0100 (CET) Subject: [py-svn] r38095 - py/trunk/py/test/rsession Message-ID: <20070207183707.355171007B@code0.codespeak.net> Author: fijal Date: Wed Feb 7 19:36:53 2007 New Revision: 38095 Modified: py/trunk/py/test/rsession/master.py Log: getremoteerror does not raise! Modified: py/trunk/py/test/rsession/master.py ============================================================================== --- py/trunk/py/test/rsession/master.py (original) +++ py/trunk/py/test/rsession/master.py Wed Feb 7 19:36:53 2007 @@ -33,17 +33,11 @@ # send start report self.reporter(repevent.SendItem(self.channel, item)) except IOError: - - try: - channel._getremoterror() - except: - # if this were not remote, we've got no clue - excinfo = py.code.ExceptionInfo() - for i in excinfo.traceback: - print str(i)[2:-1] - print excinfo - else: - raise + print "Sending error, channel IOError" + print channel._getremoterror() + # XXX: this should go as soon as we'll have proper detection + # of hanging nodes and such + raise def itemgen(colitems, reporter, keyword, reporterror): def rep(x): From fijal at codespeak.net Wed Feb 7 19:40:20 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Wed, 7 Feb 2007 19:40:20 +0100 (CET) Subject: [py-svn] r38096 - py/trunk/py/test/rsession Message-ID: <20070207184020.944DE1007B@code0.codespeak.net> Author: fijal Date: Wed Feb 7 19:40:16 2007 New Revision: 38096 Modified: py/trunk/py/test/rsession/master.py Log: typo Modified: py/trunk/py/test/rsession/master.py ============================================================================== --- py/trunk/py/test/rsession/master.py (original) +++ py/trunk/py/test/rsession/master.py Wed Feb 7 19:40:16 2007 @@ -34,7 +34,7 @@ self.reporter(repevent.SendItem(self.channel, item)) except IOError: print "Sending error, channel IOError" - print channel._getremoterror() + print self.channel._getremoterror() # XXX: this should go as soon as we'll have proper detection # of hanging nodes and such raise From hpk at codespeak.net Wed Feb 7 19:43:28 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 7 Feb 2007 19:43:28 +0100 (CET) Subject: [py-svn] r38097 - in py/trunk/py: doc path/local/testing test test/rsession test/testing Message-ID: <20070207184328.570111007B@code0.codespeak.net> Author: hpk Date: Wed Feb 7 19:43:23 2007 New Revision: 38097 Modified: py/trunk/py/doc/test.txt py/trunk/py/path/local/testing/test_local.py py/trunk/py/test/config.py py/trunk/py/test/defaultconftest.py py/trunk/py/test/rsession/rsession.py py/trunk/py/test/testing/test_collect.py py/trunk/py/test/testing/test_config.py py/trunk/py/test/testing/test_session.py Log: streamline boxed test configuration: "config.option.boxed" indicates now boxed tests and RSession.fixoptions takes care to honour dist_boxed accordingly. So you can do if not py.test.config.boxed: py.test.skip(...) i also fixed the documentation. Modified: py/trunk/py/doc/test.txt ============================================================================== --- py/trunk/py/doc/test.txt (original) +++ py/trunk/py/doc/test.txt Wed Feb 7 19:43:23 2007 @@ -461,8 +461,8 @@ Run browser (implies --startserver). -``--box`` - Use boxing: run each test in an external process. Very useful for testing +``--boxed`` + Use boxed tests: run each test in an external process. Very useful for testing things that occasionally segfault (since normally the segfault then would stop the whole test process). @@ -553,7 +553,7 @@ * `dist_rsync_ignore` - a list of relative locations to ignore for rsyncing * `dist_remotepython` - the remote python executable to run. * `dist_nicelevel` - process priority of remote nodes. -* `dist_boxing` - will run each single test in a separate process +* `dist_boxed` - will run each single test in a separate process (allowing to survive segfaults for example) * `dist_taskspernode` - Maximum number of tasks being queued to remote nodes @@ -563,7 +563,7 @@ dist_rsync_roots = ['../pypy', '../py'] dist_remotepython = 'python2.4' dist_nicelevel = 10 - dist_boxing = True + dist_boxed = False dist_maxwait = 100 dist_taskspernode = 10 Modified: py/trunk/py/path/local/testing/test_local.py ============================================================================== --- py/trunk/py/path/local/testing/test_local.py (original) +++ py/trunk/py/path/local/testing/test_local.py Wed Feb 7 19:43:23 2007 @@ -261,8 +261,8 @@ assert not numdir.new(ext=str(i-3)).check() def test_locked_make_numbered_dir(self): - if py.test.config.is_boxed(): - py.test.skip("Fails under boxing") + if py.test.config.option.boxed: + py.test.skip("Fails when run as boxed tests") root = self.tmpdir for i in range(10): numdir = local.make_numbered_dir(prefix='base.', rootdir=root, Modified: py/trunk/py/test/config.py ============================================================================== --- py/trunk/py/test/config.py (original) +++ py/trunk/py/test/config.py Wed Feb 7 19:43:23 2007 @@ -156,13 +156,13 @@ if self.option.dist: name = 'RSession' else: - optnames = 'startserver runbrowser apigen restreport boxing'.split() + optnames = 'startserver runbrowser apigen restreport boxed'.split() for opt in optnames: if getattr(self.option, opt, False): name = 'LSession' break else: - if self.getvalue('dist_boxing'): + if self.getvalue('dist_boxed'): name = 'LSession' if self.option.looponfailing: name = 'RemoteTerminalSession' @@ -170,10 +170,6 @@ name = 'RemoteTerminalSession' return name - def is_boxed(self): - # XXX probably not a good idea to have this special function ... - return self.option.boxing or self.getvalue("dist_boxing") - def _reparse(self, args): """ this is used from tests that want to re-invoke parse(). """ #assert args # XXX should not be empty Modified: py/trunk/py/test/defaultconftest.py ============================================================================== --- py/trunk/py/test/defaultconftest.py (original) +++ py/trunk/py/test/defaultconftest.py Wed Feb 7 19:43:23 2007 @@ -19,7 +19,7 @@ # whole pkgdir will be rsynced dist_remotepython = "python" dist_taskspernode = 15 -dist_boxing = False +dist_boxed = False if hasattr(py.std.os, 'nice'): dist_nicelevel = py.std.os.nice(0) # nice py.test works else: @@ -87,9 +87,9 @@ action="store_true", dest="runbrowser", default=False, help="run browser (implies --startserver)." ), - Option('', '--box', - action="store_true", dest="boxing", - help="use boxing (running each test in external process)"), + Option('', '--boxed', + action="store_true", dest="boxed", default=False, + help="box each test run in a separate process"), Option('', '--rest', action='store_true', dest="restreport", default=False, help="restructured text output reporting."), Modified: py/trunk/py/test/rsession/rsession.py ============================================================================== --- py/trunk/py/test/rsession/rsession.py (original) +++ py/trunk/py/test/rsession/rsession.py Wed Feb 7 19:43:23 2007 @@ -30,6 +30,8 @@ if option.nocapture: print "Cannot use nocapture with distributed testing" sys.exit(1) + if self.config.getvalue("dist_boxed"): + option.boxed = True super(AbstractSession, self).fixoptions() def init_reporter(self, reporter, hosts, reporter_class, arg=""): @@ -240,9 +242,8 @@ module_name=pkgname) self.tracer = Tracer(self.docstorage) return apigen_runner + elif self.config.option.boxed: + return box_runner else: - if (self.config.getvalue('dist_boxing') or self.config.option.boxing)\ - and not self.config.option.nocapture: - return box_runner return plain_runner Modified: py/trunk/py/test/testing/test_collect.py ============================================================================== --- py/trunk/py/test/testing/test_collect.py (original) +++ py/trunk/py/test/testing/test_collect.py Wed Feb 7 19:43:23 2007 @@ -7,6 +7,10 @@ mod.datadir = setupdatadir() mod.tmpdir = py.test.ensuretemp('test_collect') +def skipboxed(): + if py.test.config.option.boxed: + py.test.skip("test does not work with boxed tests") + def test_failing_import_execfile(): dest = datadir / 'failingimport.py' col = py.test.collect.Module(dest) @@ -267,8 +271,7 @@ assert len(l) == 1 def test_order_of_execution_generator_same_codeline(): - if py.test.config.is_boxed(): - py.test.skip("Does not work with boxing") + skipboxed() test_list = [] expected_list = range(6) @@ -286,8 +289,7 @@ def test_order_of_execution_generator_different_codeline(): - if py.test.config.is_boxed(): - py.test.skip("Does not work with boxing") + skipboxed() test_list = [] expected_list = range(3) Modified: py/trunk/py/test/testing/test_config.py ============================================================================== --- py/trunk/py/test/testing/test_config.py (original) +++ py/trunk/py/test/testing/test_config.py Wed Feb 7 19:43:23 2007 @@ -207,7 +207,7 @@ assert config._getsessionname() == 'RSession' def test_implied_lsession(self): - optnames = 'startserver runbrowser apigen=x rest box'.split() + optnames = 'startserver runbrowser apigen=x rest boxed'.split() for x in optnames: config = py.test.config._reparse([self.tmpdir, '--%s' % x]) assert config._getsessionname() == 'LSession' @@ -240,22 +240,30 @@ session = config.initsession() assert session.config is config - def test_boxing_options(self): - # XXX config.is_boxed() is probably not a good idea - tmpdir = self.tmpdir - config = py.test.config._reparse([tmpdir]) - assert not config.option.boxing - assert not config.is_boxed() - - #tmpdir.join("conftest.py").write("dist_boxing=True\n") - #config = py.test.config._reparse([tmpdir]) - #assert config.is_boxed() - - tmpdir.join("conftest.py").write("dist_boxing=False\n") + def test_boxed_option_including_implied_from_conftest(self): + self.tmpdir.join("conftest.py").write("dist_hosts=[]") + tmpdir = self.tmpdir.ensure("subdir", dir=1) config = py.test.config._reparse([tmpdir]) - assert not config.is_boxed() + config.initsession() + assert not config.option.boxed + config = py.test.config._reparse(['--dist', tmpdir]) + config.initsession() + assert not config.option.boxed + + tmpdir.join("conftest.py").write(py.code.Source(""" + dist_hosts = [] + dist_boxed = True + """)) + config = py.test.config._reparse(['--dist', tmpdir]) + config.initsession() + assert config.option.boxed + tmpdir.join("conftest.py").write(py.code.Source(""" + dist_boxed = False + """)) config = py.test.config._reparse([tmpdir, '--box']) - assert config.is_boxed() + assert config.option.boxed + config.initsession() + assert config.option.boxed def test_getvalue_pathlist(self): tmpdir = self.tmpdir Modified: py/trunk/py/test/testing/test_session.py ============================================================================== --- py/trunk/py/test/testing/test_session.py (original) +++ py/trunk/py/test/testing/test_session.py Wed Feb 7 19:43:23 2007 @@ -50,6 +50,10 @@ l = session.getitemoutcomepairs(Passed) assert not l +def test_is_not_boxed_by_default(): + config = py.test.config._reparse([datadir]) + assert not config.option.boxed + class TestKeywordSelection: def test_select_simple(self): for keyword in ['test_one', 'est_on']: From fijal at codespeak.net Wed Feb 7 19:44:55 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Wed, 7 Feb 2007 19:44:55 +0100 (CET) Subject: [py-svn] r38098 - in py/trunk/py/test/rsession: . testing Message-ID: <20070207184455.1359F1007B@code0.codespeak.net> Author: fijal Date: Wed Feb 7 19:44:49 2007 New Revision: 38098 Modified: py/trunk/py/test/rsession/master.py py/trunk/py/test/rsession/testing/test_master.py Log: A test and typo discovered by that test. Modified: py/trunk/py/test/rsession/master.py ============================================================================== --- py/trunk/py/test/rsession/master.py (original) +++ py/trunk/py/test/rsession/master.py Wed Feb 7 19:44:49 2007 @@ -34,7 +34,7 @@ self.reporter(repevent.SendItem(self.channel, item)) except IOError: print "Sending error, channel IOError" - print self.channel._getremoterror() + print self.channel._getremoteerror() # XXX: this should go as soon as we'll have proper detection # of hanging nodes and such raise Modified: py/trunk/py/test/rsession/testing/test_master.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_master.py (original) +++ py/trunk/py/test/rsession/testing/test_master.py Wed Feb 7 19:44:49 2007 @@ -39,6 +39,16 @@ assert py.std.marshal.dumps(item) self.sent.append(item) +class NonWorkingChannel(object): + def setcallback(self, func): + pass + + def send(self, item): + raise IOError + + def _getremoteerror(self): + return "blah" + class Item(py.test.Item): def _get_collector_trail(self): return (self.name,) @@ -62,6 +72,15 @@ assert received[0].outcome.passed assert not received[1].outcome.passed +def test_masternode_nonworking_channel(): + ch = NonWorkingChannel() + reportlist = [] + mnode = MasterNode(ch, reportlist.append) + cap = py.io.StdCaptureFD() + py.test.raises(IOError, 'mnode.send(Item("ok"))') + out, err = cap.reset() + assert out.find("blah") != -1 + def test_sending_two_noes(): # XXX fijal: this test previously tested that the second # item result would not get send. why? did i miss From fijal at codespeak.net Wed Feb 7 19:54:59 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Wed, 7 Feb 2007 19:54:59 +0100 (CET) Subject: [py-svn] r38099 - py/trunk/py/test/rsession Message-ID: <20070207185459.A3E2E1007B@code0.codespeak.net> Author: fijal Date: Wed Feb 7 19:54:51 2007 New Revision: 38099 Modified: py/trunk/py/test/rsession/rsession.py Log: * RSession cannot work with nocapture, LSession can * RSession is always boxed Modified: py/trunk/py/test/rsession/rsession.py ============================================================================== --- py/trunk/py/test/rsession/rsession.py (original) +++ py/trunk/py/test/rsession/rsession.py Wed Feb 7 19:54:51 2007 @@ -27,9 +27,6 @@ if option.runbrowser and not option.startserver: #print "--runbrowser implies --startserver" option.startserver = True - if option.nocapture: - print "Cannot use nocapture with distributed testing" - sys.exit(1) if self.config.getvalue("dist_boxed"): option.boxed = True super(AbstractSession, self).fixoptions() @@ -111,7 +108,12 @@ """ def fixoptions(self): super(RSession, self).fixoptions() + option = self.config.option + if option.nocapture: + print "Cannot use nocapture with distributed testing" + sys.exit(1) config = self.config + config.option.boxed = True try: config.getvalue('dist_hosts') except KeyError: From fijal at codespeak.net Wed Feb 7 19:55:46 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Wed, 7 Feb 2007 19:55:46 +0100 (CET) Subject: [py-svn] r38100 - py/trunk/py/test/rsession Message-ID: <20070207185546.8835010081@code0.codespeak.net> Author: fijal Date: Wed Feb 7 19:55:39 2007 New Revision: 38100 Modified: py/trunk/py/test/rsession/slave.py Log: Slave is always boxed as well (different level of skips) Modified: py/trunk/py/test/rsession/slave.py ============================================================================== --- py/trunk/py/test/rsession/slave.py (original) +++ py/trunk/py/test/rsession/slave.py Wed Feb 7 19:55:39 2007 @@ -133,6 +133,7 @@ sys.path.insert(0, basedir) import py config = py.test.config + config.option.boxed = True assert not config._initialized config.initdirect(basedir, config_repr) if not config.option.nomagic: From fijal at codespeak.net Wed Feb 7 19:56:16 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Wed, 7 Feb 2007 19:56:16 +0100 (CET) Subject: [py-svn] r38101 - py/trunk/py/test/rsession Message-ID: <20070207185616.411B31007B@code0.codespeak.net> Author: fijal Date: Wed Feb 7 19:56:10 2007 New Revision: 38101 Modified: py/trunk/py/test/rsession/slave.py Log: oops, this will be implied anyway Modified: py/trunk/py/test/rsession/slave.py ============================================================================== --- py/trunk/py/test/rsession/slave.py (original) +++ py/trunk/py/test/rsession/slave.py Wed Feb 7 19:56:10 2007 @@ -133,7 +133,6 @@ sys.path.insert(0, basedir) import py config = py.test.config - config.option.boxed = True assert not config._initialized config.initdirect(basedir, config_repr) if not config.option.nomagic: From fijal at codespeak.net Wed Feb 7 19:56:57 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Wed, 7 Feb 2007 19:56:57 +0100 (CET) Subject: [py-svn] r38102 - py/trunk/py/test/testing Message-ID: <20070207185657.4D64C10081@code0.codespeak.net> Author: fijal Date: Wed Feb 7 19:56:48 2007 New Revision: 38102 Modified: py/trunk/py/test/testing/test_config.py Log: Skip this test not to interfere with py.test.config.boxed Modified: py/trunk/py/test/testing/test_config.py ============================================================================== --- py/trunk/py/test/testing/test_config.py (original) +++ py/trunk/py/test/testing/test_config.py Wed Feb 7 19:56:48 2007 @@ -2,6 +2,7 @@ import py from py.__.test.config import gettopdir +from py.__.test.testing.test_collect import skipboxed def test_tmpdir(): d1 = py.test.ensuretemp('hello') @@ -241,14 +242,15 @@ assert session.config is config def test_boxed_option_including_implied_from_conftest(self): + skipboxed() self.tmpdir.join("conftest.py").write("dist_hosts=[]") tmpdir = self.tmpdir.ensure("subdir", dir=1) config = py.test.config._reparse([tmpdir]) config.initsession() - assert not config.option.boxed + assert not config.option.boxed config = py.test.config._reparse(['--dist', tmpdir]) config.initsession() - assert not config.option.boxed + assert not config.option.boxed tmpdir.join("conftest.py").write(py.code.Source(""" dist_hosts = [] From fijal at codespeak.net Wed Feb 7 20:02:29 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Wed, 7 Feb 2007 20:02:29 +0100 (CET) Subject: [py-svn] r38103 - py/trunk/py/test/testing Message-ID: <20070207190229.4D51D10084@code0.codespeak.net> Author: fijal Date: Wed Feb 7 20:02:21 2007 New Revision: 38103 Modified: py/trunk/py/test/testing/test_config.py Log: Hum. This test was just broken. Fix it and add additional checks. Modified: py/trunk/py/test/testing/test_config.py ============================================================================== --- py/trunk/py/test/testing/test_config.py (original) +++ py/trunk/py/test/testing/test_config.py Wed Feb 7 20:02:21 2007 @@ -242,7 +242,6 @@ assert session.config is config def test_boxed_option_including_implied_from_conftest(self): - skipboxed() self.tmpdir.join("conftest.py").write("dist_hosts=[]") tmpdir = self.tmpdir.ensure("subdir", dir=1) config = py.test.config._reparse([tmpdir]) @@ -250,7 +249,7 @@ assert not config.option.boxed config = py.test.config._reparse(['--dist', tmpdir]) config.initsession() - assert not config.option.boxed + assert config.option.boxed tmpdir.join("conftest.py").write(py.code.Source(""" dist_hosts = [] @@ -265,7 +264,13 @@ config = py.test.config._reparse([tmpdir, '--box']) assert config.option.boxed config.initsession() - assert config.option.boxed + assert config.option.boxed + config = py.test.config._reparse([tmpdir, '-d']) + assert not config.option.boxed + config.initsession() + assert config.option.boxed + config = py.test.config._reparse([tmpdir, '-d', '-s']) + py.test.raises(SystemExit, "config.initsession()") def test_getvalue_pathlist(self): tmpdir = self.tmpdir From hpk at codespeak.net Wed Feb 7 20:04:43 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 7 Feb 2007 20:04:43 +0100 (CET) Subject: [py-svn] r38104 - py/trunk/py/doc Message-ID: <20070207190443.249B510087@code0.codespeak.net> Author: hpk Date: Wed Feb 7 20:04:35 2007 New Revision: 38104 Modified: py/trunk/py/doc/io.txt Log: fixing this so "py.test -s" passes on this as well Modified: py/trunk/py/doc/io.txt ============================================================================== --- py/trunk/py/doc/io.txt (original) +++ py/trunk/py/doc/io.txt Wed Feb 7 20:04:35 2007 @@ -33,13 +33,12 @@ :api:`py.io.StdCaptureFD` --------------------------- -If you also want to capture writes to the stdout/stdin +If you also want to capture writes to the stdout/stderr filedescriptors you may invoke: >>> import py, sys >>> capture = py.io.StdCaptureFD() - >>> sys.stdout.write("hello") >>> sys.stderr.write("world") >>> out,err = capture.reset() - >>> out.strip() + err.strip() == "helloworld" - True + >>> err + 'world' From fijal at codespeak.net Wed Feb 7 20:14:13 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Wed, 7 Feb 2007 20:14:13 +0100 (CET) Subject: [py-svn] r38105 - in py/trunk/py/test/rsession: . webdata Message-ID: <20070207191413.7755010087@code0.codespeak.net> Author: fijal Date: Wed Feb 7 20:14:10 2007 New Revision: 38105 Modified: py/trunk/py/test/rsession/webdata/index.html py/trunk/py/test/rsession/webdata/source.js py/trunk/py/test/rsession/webjs.py Log: Make errors bright red and bold, easier to spot. Modified: py/trunk/py/test/rsession/webdata/index.html ============================================================================== --- py/trunk/py/test/rsession/webdata/index.html (original) +++ py/trunk/py/test/rsession/webdata/index.html Wed Feb 7 20:14:10 2007 @@ -20,6 +20,11 @@ z-index: 2; } + .error { + color: #F00; + font-weight: bold; + } + #navbar { position: fixed; right: 1em; Modified: py/trunk/py/test/rsession/webdata/source.js ============================================================================== Binary files. No diff available. Modified: py/trunk/py/test/rsession/webjs.py ============================================================================== --- py/trunk/py/test/rsession/webjs.py (original) +++ py/trunk/py/test/rsession/webjs.py Wed Feb 7 20:14:10 2007 @@ -151,6 +151,7 @@ link.setAttribute("href", "javascript:show_traceback('%s')" % ( msg['fullitemname'],)) txt = create_text_elem('F') + link.setAttribute('class', 'error') link.appendChild(txt) td.appendChild(link) exported_methods.show_fail(item_name, fail_come_back) From hpk at codespeak.net Wed Feb 7 20:16:29 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 7 Feb 2007 20:16:29 +0100 (CET) Subject: [py-svn] r38106 - py/trunk/py/test/testing Message-ID: <20070207191629.7AF5310087@code0.codespeak.net> Author: hpk Date: Wed Feb 7 20:16:28 2007 New Revision: 38106 Modified: py/trunk/py/test/testing/test_collect.py Log: making tests independent of --dist implications Modified: py/trunk/py/test/testing/test_collect.py ============================================================================== --- py/trunk/py/test/testing/test_collect.py (original) +++ py/trunk/py/test/testing/test_collect.py Wed Feb 7 20:16:28 2007 @@ -271,46 +271,62 @@ assert len(l) == 1 def test_order_of_execution_generator_same_codeline(): - skipboxed() - test_list = [] - expected_list = range(6) - - def list_append(item): - test_list.append(item) - - def assert_order_of_execution(): - print 'expected order', expected_list - print 'but got ', test_list - assert test_list == expected_list - - for i in expected_list: - yield list_append, i - yield assert_order_of_execution - + o = tmpdir.ensure('genorder1', dir=1) + o.join("test_order1.py").write(py.code.Source(""" + def test_generative_order_of_execution(): + test_list = [] + expected_list = range(6) + + def list_append(item): + test_list.append(item) + + def assert_order_of_execution(): + print 'expected order', expected_list + print 'but got ', test_list + assert test_list == expected_list + + for i in expected_list: + yield list_append, i + yield assert_order_of_execution + """)) + config = py.test.config._reparse([o]) + session = config.initsession() + session.main() + l = session.getitemoutcomepairs(Passed) + assert len(l) == 7 def test_order_of_execution_generator_different_codeline(): - skipboxed() - test_list = [] - expected_list = range(3) - - def list_append_2(): - test_list.append(2) - - def list_append_1(): - test_list.append(1) - - def list_append_0(): - test_list.append(0) - - def assert_order_of_execution(): - print 'expected order', expected_list - print 'but got ', test_list - assert test_list == expected_list - - yield list_append_0 - yield list_append_1 - yield list_append_2 - yield assert_order_of_execution + o = tmpdir.ensure('genorder2', dir=2) + o.join("test_genorder2.py").write(py.code.Source(""" + def test_generative_tests_different_codeline(): + test_list = [] + expected_list = range(3) + + def list_append_2(): + test_list.append(2) + + def list_append_1(): + test_list.append(1) + + def list_append_0(): + test_list.append(0) + + def assert_order_of_execution(): + print 'expected order', expected_list + print 'but got ', test_list + assert test_list == expected_list + + yield list_append_0 + yield list_append_1 + yield list_append_2 + yield assert_order_of_execution + """)) + config = py.test.config._reparse([o]) + session = config.initsession() + session.main() + l = session.getitemoutcomepairs(Passed) + assert len(l) == 4 + def test_documentation_virtual_collector_interaction(): From fijal at codespeak.net Wed Feb 7 20:24:39 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Wed, 7 Feb 2007 20:24:39 +0100 (CET) Subject: [py-svn] r38107 - in py/trunk/py/test/rsession: . testing Message-ID: <20070207192439.EE1D410087@code0.codespeak.net> Author: fijal Date: Wed Feb 7 20:24:38 2007 New Revision: 38107 Modified: py/trunk/py/test/rsession/hostmanage.py py/trunk/py/test/rsession/testing/test_hostmanage.py Log: * Have optimise_localhost flag and a test for it (but not turning it on by default, this will be done later) * two other occurences of possibly-failing channel.send Modified: py/trunk/py/test/rsession/hostmanage.py ============================================================================== --- py/trunk/py/test/rsession/hostmanage.py (original) +++ py/trunk/py/test/rsession/hostmanage.py Wed Feb 7 20:24:38 2007 @@ -106,7 +106,8 @@ return remotepath class HostManager(object): - def __init__(self, config, hosts=None): + def __init__(self, config, hosts=None, optimise_localhost=False): + self.optimise_localhost = optimise_localhost self.config = config if hosts is None: hosts = self.config.getvalue("dist_hosts") @@ -132,14 +133,21 @@ rsync = HostRSync(ignores=ignores, verbose=self.config.option.verbose) destrelpath = root.relto(self.config.topdir) + to_send = False for host in self.hosts: - def donecallback(host, root): + if host.hostname != 'localhost' or not self.optimise_localhost: + def donecallback(host=host, root=root): + reporter(repevent.HostRSyncRootReady(host, root)) + remotepath = rsync.add_target_host( + host, reporter, destrelpath, finishedcallback= + donecallback) + reporter(repevent.HostRSyncing(host, root, remotepath)) + to_send = True + else: reporter(repevent.HostRSyncRootReady(host, root)) - remotepath = rsync.add_target_host( - host, reporter, destrelpath, finishedcallback= - lambda host=host, root=root: donecallback(host, root)) - reporter(repevent.HostRSyncing(host, root, remotepath)) - rsync.send(root) + if to_send: + # don't send if we have no targets + rsync.send(root) def setup_hosts(self, reporter): self.init_rsync(reporter) @@ -153,7 +161,14 @@ def teardown_hosts(self, reporter, channels, nodes, waiter=lambda : time.sleep(.1), exitfirst=False): for channel in channels: - channel.send(None) + try: + channel.send(None) + except IOError: + print "Sending error, channel IOError" + print channel._getremoterror() + # XXX: this should go as soon as we'll have proper detection + # of hanging nodes and such + raise clean = exitfirst while not clean: @@ -166,7 +181,11 @@ def kill_channels(self, channels): for channel in channels: - channel.send(42) + try: + channel.send(42) + except IOError: + print "Sending error, channel IOError" + print channel._getremoterror() def teardown_gateways(self, reporter, channels): for channel in channels: Modified: py/trunk/py/test/rsession/testing/test_hostmanage.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_hostmanage.py (original) +++ py/trunk/py/test/rsession/testing/test_hostmanage.py Wed Feb 7 20:24:38 2007 @@ -192,6 +192,19 @@ assert self.dest.join("dir5","file").check() assert not self.dest.join("dir6").check() + def test_hostmanager_rsync_optimise_localhost(self): + dir1 = self.source.ensure("opt_localhost1", dir=1) + config = py.test.config._reparse([self.source]) + hm = HostManager(config, + hosts=[HostInfo("localhost:" + str(self.dest))], + optimise_localhost=True) + events = [] + hm.init_rsync(reporter=events.append) + rr = [i for i in events if isinstance(i, repevent.HostRSyncRootReady)] + assert len(rr) == 1 + hr = [i for i in events if isinstance(i, repevent.HostRSyncing)] + assert len(hr) == 0 + def test_getpath_relto_home(): x = getpath_relto_home("hello") assert x == py.path.local._gethomedir().join("hello") From hpk at codespeak.net Wed Feb 7 20:28:30 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 7 Feb 2007 20:28:30 +0100 (CET) Subject: [py-svn] r38108 - py/trunk/py/misc/testing Message-ID: <20070207192830.6E50A1007B@code0.codespeak.net> Author: hpk Date: Wed Feb 7 20:28:29 2007 New Revision: 38108 Modified: py/trunk/py/misc/testing/test_initpkg.py Log: skip this test if we have no .svn dir Modified: py/trunk/py/misc/testing/test_initpkg.py ============================================================================== --- py/trunk/py/misc/testing/test_initpkg.py (original) +++ py/trunk/py/misc/testing/test_initpkg.py Wed Feb 7 20:28:29 2007 @@ -88,6 +88,8 @@ s = py.__package__.getzipdata() def test_getrev(): + if not py.path.local(py.__file__).dirpath('.svn').check(): + py.test.skip("py package is not a svn checkout") d = py.__package__.getrev() svnversion = py.path.local.sysfind('svnversion') if svnversion is None: From hpk at codespeak.net Wed Feb 7 20:36:43 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 7 Feb 2007 20:36:43 +0100 (CET) Subject: [py-svn] r38109 - in py/trunk/py/test/rsession: . testing Message-ID: <20070207193643.4923C1007B@code0.codespeak.net> Author: hpk Date: Wed Feb 7 20:36:41 2007 New Revision: 38109 Modified: py/trunk/py/test/rsession/hostmanage.py py/trunk/py/test/rsession/testing/test_hostmanage.py Log: bailing out the optimize_localhost approach to consider it differently Modified: py/trunk/py/test/rsession/hostmanage.py ============================================================================== --- py/trunk/py/test/rsession/hostmanage.py (original) +++ py/trunk/py/test/rsession/hostmanage.py Wed Feb 7 20:36:41 2007 @@ -106,8 +106,7 @@ return remotepath class HostManager(object): - def __init__(self, config, hosts=None, optimise_localhost=False): - self.optimise_localhost = optimise_localhost + def __init__(self, config, hosts=None): self.config = config if hosts is None: hosts = self.config.getvalue("dist_hosts") @@ -133,21 +132,14 @@ rsync = HostRSync(ignores=ignores, verbose=self.config.option.verbose) destrelpath = root.relto(self.config.topdir) - to_send = False for host in self.hosts: - if host.hostname != 'localhost' or not self.optimise_localhost: - def donecallback(host=host, root=root): - reporter(repevent.HostRSyncRootReady(host, root)) - remotepath = rsync.add_target_host( - host, reporter, destrelpath, finishedcallback= - donecallback) - reporter(repevent.HostRSyncing(host, root, remotepath)) - to_send = True - else: + def donecallback(host, root): reporter(repevent.HostRSyncRootReady(host, root)) - if to_send: - # don't send if we have no targets - rsync.send(root) + remotepath = rsync.add_target_host( + host, reporter, destrelpath, finishedcallback= + lambda host=host, root=root: donecallback(host, root)) + reporter(repevent.HostRSyncing(host, root, remotepath)) + rsync.send(root) def setup_hosts(self, reporter): self.init_rsync(reporter) @@ -161,14 +153,7 @@ def teardown_hosts(self, reporter, channels, nodes, waiter=lambda : time.sleep(.1), exitfirst=False): for channel in channels: - try: - channel.send(None) - except IOError: - print "Sending error, channel IOError" - print channel._getremoterror() - # XXX: this should go as soon as we'll have proper detection - # of hanging nodes and such - raise + channel.send(None) clean = exitfirst while not clean: @@ -181,11 +166,7 @@ def kill_channels(self, channels): for channel in channels: - try: - channel.send(42) - except IOError: - print "Sending error, channel IOError" - print channel._getremoterror() + channel.send(42) def teardown_gateways(self, reporter, channels): for channel in channels: Modified: py/trunk/py/test/rsession/testing/test_hostmanage.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_hostmanage.py (original) +++ py/trunk/py/test/rsession/testing/test_hostmanage.py Wed Feb 7 20:36:41 2007 @@ -192,19 +192,6 @@ assert self.dest.join("dir5","file").check() assert not self.dest.join("dir6").check() - def test_hostmanager_rsync_optimise_localhost(self): - dir1 = self.source.ensure("opt_localhost1", dir=1) - config = py.test.config._reparse([self.source]) - hm = HostManager(config, - hosts=[HostInfo("localhost:" + str(self.dest))], - optimise_localhost=True) - events = [] - hm.init_rsync(reporter=events.append) - rr = [i for i in events if isinstance(i, repevent.HostRSyncRootReady)] - assert len(rr) == 1 - hr = [i for i in events if isinstance(i, repevent.HostRSyncing)] - assert len(hr) == 0 - def test_getpath_relto_home(): x = getpath_relto_home("hello") assert x == py.path.local._gethomedir().join("hello") From hpk at codespeak.net Wed Feb 7 20:41:53 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 7 Feb 2007 20:41:53 +0100 (CET) Subject: [py-svn] r38110 - py/trunk/py/rest/testing Message-ID: <20070207194153.5BD7B10077@code0.codespeak.net> Author: hpk Date: Wed Feb 7 20:41:50 2007 New Revision: 38110 Modified: py/trunk/py/rest/testing/test_directive.py Log: i don't think one needs to use svnwc's anymore, at least it works for me, i think. Modified: py/trunk/py/rest/testing/test_directive.py ============================================================================== --- py/trunk/py/rest/testing/test_directive.py (original) +++ py/trunk/py/rest/testing/test_directive.py Wed Feb 7 20:41:50 2007 @@ -15,10 +15,9 @@ if not py.path.local.sysfind("dot"): py.test.skip("graphviz needed") directive.set_backend_and_register_directives("html") - #for reasons that elude me rest.process expects svnwcs??? if not py.path.local.sysfind("svn"): py.test.skip("svn needed") - txt = py.path.svnwc(datadir.join("graphviz.txt")) + txt = datadir.join("graphviz.txt") html = txt.new(ext="html") png = datadir.join("example1.png") rest.process(txt) From hpk at codespeak.net Wed Feb 7 20:52:17 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 7 Feb 2007 20:52:17 +0100 (CET) Subject: [py-svn] r38111 - py/trunk/py/path/svn/testing Message-ID: <20070207195217.D339F10083@code0.codespeak.net> Author: hpk Date: Wed Feb 7 20:52:14 2007 New Revision: 38111 Modified: py/trunk/py/path/svn/testing/test_test_repo.py Log: remove test order dependency Modified: py/trunk/py/path/svn/testing/test_test_repo.py ============================================================================== --- py/trunk/py/path/svn/testing/test_test_repo.py (original) +++ py/trunk/py/path/svn/testing/test_test_repo.py Wed Feb 7 20:52:14 2007 @@ -15,6 +15,7 @@ assert len(self.wc.listdir()) == 0 def test_commit(self): + self.wc.checkout(self.repo) p = self.wc.join("a_file") p.write("test file") p.add() From fijal at codespeak.net Wed Feb 7 20:53:04 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Wed, 7 Feb 2007 20:53:04 +0100 (CET) Subject: [py-svn] r38112 - py/trunk/py/test/rsession Message-ID: <20070207195304.EB28F10084@code0.codespeak.net> Author: fijal Date: Wed Feb 7 20:53:03 2007 New Revision: 38112 Modified: py/trunk/py/test/rsession/executor.py Log: This time don't catch SystemExit. Increases stability Modified: py/trunk/py/test/rsession/executor.py ============================================================================== --- py/trunk/py/test/rsession/executor.py (original) +++ py/trunk/py/test/rsession/executor.py Wed Feb 7 20:53:03 2007 @@ -29,8 +29,6 @@ outcome = Outcome() except Skipped, e: outcome = Outcome(skipped=str(e)) - except (KeyboardInterrupt, SystemExit): - raise except: excinfo = py.code.ExceptionInfo() if isinstance(self.item, py.test.Function): From fijal at codespeak.net Wed Feb 7 21:04:09 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Wed, 7 Feb 2007 21:04:09 +0100 (CET) Subject: [py-svn] r38113 - py/trunk/py/test/rsession Message-ID: <20070207200409.F3A7910083@code0.codespeak.net> Author: fijal Date: Wed Feb 7 21:04:01 2007 New Revision: 38113 Modified: py/trunk/py/test/rsession/executor.py Log: Make fatal() attribute which makes boxed tests catch SystemExit, while in-process let it slide. Modified: py/trunk/py/test/rsession/executor.py ============================================================================== --- py/trunk/py/test/rsession/executor.py (original) +++ py/trunk/py/test/rsession/executor.py Wed Feb 7 21:04:01 2007 @@ -22,6 +22,9 @@ def run(self): self.item.run() + + def fatals(self): + return (SystemExit, KeyboardInterrupt) def execute(self): try: @@ -29,6 +32,8 @@ outcome = Outcome() except Skipped, e: outcome = Outcome(skipped=str(e)) + except self.fatals(): + raise except: excinfo = py.code.ExceptionInfo() if isinstance(self.item, py.test.Function): @@ -78,7 +83,10 @@ """ Same as RunExecutor, but boxes test instead """ wraps = True - + + def fatals(self): + return None + def execute(self): def fun(): outcome = RunExecutor.execute(self) @@ -100,6 +108,9 @@ computations (more async mode) """ wraps = True + + def fatals(self): + return None def execute(self): def fun(): From fijal at codespeak.net Wed Feb 7 21:14:17 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Wed, 7 Feb 2007 21:14:17 +0100 (CET) Subject: [py-svn] r38114 - py/trunk/py/test/rsession Message-ID: <20070207201417.E052D10087@code0.codespeak.net> Author: fijal Date: Wed Feb 7 21:14:06 2007 New Revision: 38114 Modified: py/trunk/py/test/rsession/executor.py Log: Kill fatals() to be considered in future Modified: py/trunk/py/test/rsession/executor.py ============================================================================== --- py/trunk/py/test/rsession/executor.py (original) +++ py/trunk/py/test/rsession/executor.py Wed Feb 7 21:14:06 2007 @@ -23,17 +23,12 @@ def run(self): self.item.run() - def fatals(self): - return (SystemExit, KeyboardInterrupt) - def execute(self): try: self.run() outcome = Outcome() except Skipped, e: outcome = Outcome(skipped=str(e)) - except self.fatals(): - raise except: excinfo = py.code.ExceptionInfo() if isinstance(self.item, py.test.Function): @@ -84,9 +79,6 @@ """ wraps = True - def fatals(self): - return None - def execute(self): def fun(): outcome = RunExecutor.execute(self) @@ -109,9 +101,6 @@ """ wraps = True - def fatals(self): - return None - def execute(self): def fun(): outcome = RunExecutor.execute(self) From fijal at codespeak.net Wed Feb 7 21:40:14 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Wed, 7 Feb 2007 21:40:14 +0100 (CET) Subject: [py-svn] r38115 - py/trunk/py/apigen/testing Message-ID: <20070207204014.66FFE10070@code0.codespeak.net> Author: fijal Date: Wed Feb 7 21:40:09 2007 New Revision: 38115 Modified: py/trunk/py/apigen/testing/test_apigen_functional.py Log: Call python directly Modified: py/trunk/py/apigen/testing/test_apigen_functional.py ============================================================================== --- py/trunk/py/apigen/testing/test_apigen_functional.py (original) +++ py/trunk/py/apigen/testing/test_apigen_functional.py Wed Feb 7 21:40:09 2007 @@ -6,6 +6,7 @@ import py from py.__.apigen import apigen + def setup_fs_project(name): temp = py.test.ensuretemp(name) assert temp.listdir() == [] @@ -123,7 +124,7 @@ 'python "%s/bin/py.test"') % (tempdir, fs_root, pydir) else: cmd = ('APIGEN_TARGET="%s" PYTHONPATH="%s" ' - '"%s/bin/py.test"') % (tempdir, fs_root, pydir) + 'python "%s/bin/py.test"') % (tempdir, fs_root, pydir) try: output = py.process.cmdexec('%s --apigen="%s/apigen.py" "%s"' % ( cmd, fs_root, pakdir)) From hpk at codespeak.net Wed Feb 7 21:40:41 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 7 Feb 2007 21:40:41 +0100 (CET) Subject: [py-svn] r38116 - in py/trunk/py/path: svn/testing testing Message-ID: <20070207204041.A3B1910084@code0.codespeak.net> Author: hpk Date: Wed Feb 7 21:40:37 2007 New Revision: 38116 Modified: py/trunk/py/path/svn/testing/svntestbase.py py/trunk/py/path/svn/testing/test_urlcommand.py py/trunk/py/path/svn/testing/test_wccommand.py py/trunk/py/path/testing/common.py Log: fixing svn tests for -d runs (which usually is a dependency problem) Modified: py/trunk/py/path/svn/testing/svntestbase.py ============================================================================== --- py/trunk/py/path/svn/testing/svntestbase.py (original) +++ py/trunk/py/path/svn/testing/svntestbase.py Wed Feb 7 21:40:37 2007 @@ -10,21 +10,16 @@ # make a wc directory out of a given root url # cache previously obtained wcs! # -def getrepowc(reponame='a~bc$aaa~', wcname='wc'): +def getrepowc(reponame='basetestrepo', wcname='wc'): repo = py.test.ensuretemp(reponame) wcdir = py.test.ensuretemp(wcname) if not repo.listdir(): #assert not wcdir.check() repo.ensure(dir=1) - try: - py.process.cmdexec('svnadmin create "%s"' % - svncommon._escape_helper(repo)) - py.process.cmdexec('svnadmin load -q "%s" <"%s"' % - (svncommon._escape_helper(repo), repodump)) - except py.process.cmdexec.Error: - raise - repo.remove() - raise py.test.skip('could not create temporary svn test repository') + py.process.cmdexec('svnadmin create "%s"' % + svncommon._escape_helper(repo)) + py.process.cmdexec('svnadmin load -q "%s" <"%s"' % + (svncommon._escape_helper(repo), repodump)) print "created svn repository", repo wcdir.ensure(dir=1) wc = py.path.svnwc(wcdir) Modified: py/trunk/py/path/svn/testing/test_urlcommand.py ============================================================================== --- py/trunk/py/path/svn/testing/test_urlcommand.py (original) +++ py/trunk/py/path/svn/testing/test_urlcommand.py Wed Feb 7 21:40:37 2007 @@ -9,11 +9,27 @@ if py.path.local.sysfind('svn') is None: py.test.skip("cannot test py.path.svn, 'svn' binary not found") -class TestSvnCommandPath(CommonCommandAndBindingTests): +class TestSvnURLCommandPath(CommonCommandAndBindingTests): def setup_class(cls): repo, wc = getrepowc() cls.root = py.path.svnurl(repo) + def test_move_file(self): # overrides base class + p = self.root.ensure('origfile') + newp = p.dirpath('newfile') + p.move(newp) + assert newp.check(file=1) + newp.remove() + assert not p.check() + + def test_move_dir(self): # overrides base class + p = self.root.ensure('origdir', dir=1) + newp = p.dirpath('newdir') + p.move(newp) + assert newp.check(dir=1) + newp.remove() + assert not p.check() + def test_svnurl_needs_arg(self): py.test.raises(TypeError, "py.path.svnurl()") @@ -52,7 +68,9 @@ assert info.last_author == 'hpk' assert info.created_rev == 2256 assert info.kind == 'file' - assert time.gmtime(info.mtime)[:6] == (2006, 11, 24, 17, 55, 0) + # we don't check for the year (2006), because that depends + # on the clock correctly being setup + assert time.gmtime(info.mtime)[1:6] == (11, 24, 17, 55, 0) assert info.size == 165 assert info.time == info.mtime * 1000000 Modified: py/trunk/py/path/svn/testing/test_wccommand.py ============================================================================== --- py/trunk/py/path/svn/testing/test_wccommand.py (original) +++ py/trunk/py/path/svn/testing/test_wccommand.py Wed Feb 7 21:40:37 2007 @@ -14,6 +14,18 @@ def setup_class(cls): repo, cls.root = getrepowc() + def test_move_file(self): # overrides base class + try: + super(TestWCSvnCommandPath, self).test_move_file() + finally: + self.root.revert(rec=1) + + def test_move_directory(self): # overrides base class + try: + super(TestWCSvnCommandPath, self).test_move_directory() + finally: + self.root.revert(rec=1) + def test_status_attributes_simple(self): def assert_nochange(p): s = p.status() Modified: py/trunk/py/path/testing/common.py ============================================================================== --- py/trunk/py/path/testing/common.py (original) +++ py/trunk/py/path/testing/common.py Wed Feb 7 21:40:37 2007 @@ -1,7 +1,7 @@ from py.__.path.common import checker import py -class CommonPathTests: +class CommonPathTests(object): root = None # subclasses have to setup a 'root' attribute def test_constructor_equality(self): From fijal at codespeak.net Wed Feb 7 21:41:58 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Wed, 7 Feb 2007 21:41:58 +0100 (CET) Subject: [py-svn] r38117 - py/trunk/py/bin Message-ID: <20070207204158.CDBDD10070@code0.codespeak.net> Author: fijal Date: Wed Feb 7 21:41:55 2007 New Revision: 38117 Modified: py/trunk/py/bin/_update_website.py Log: call "python py.test" instead of "py.test" (does not rely on +x flag) Modified: py/trunk/py/bin/_update_website.py ============================================================================== --- py/trunk/py/bin/_update_website.py (original) +++ py/trunk/py/bin/_update_website.py Wed Feb 7 21:41:55 2007 @@ -32,7 +32,8 @@ apigenpath = pkgpath.join('apigen/apigen.py') # XXX be more general here? if not apigenpath.check(file=True): apigenpath = pypath.join('apigen/apigen.py') - cmd = 'PYTHONPATH="%s:%s" "%s" --apigen="%s" "%s" %s' % (pypath.dirpath(), + cmd = 'PYTHONPATH="%s:%s" python "%s" --apigen="%s" "%s" %s' % ( + pypath.dirpath(), pkgpath.dirpath(), pytestpath, apigenpath, From hpk at codespeak.net Wed Feb 7 21:48:58 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 7 Feb 2007 21:48:58 +0100 (CET) Subject: [py-svn] r38118 - py/trunk/py/doc Message-ID: <20070207204858.A64C710061@code0.codespeak.net> Author: hpk Date: Wed Feb 7 21:48:49 2007 New Revision: 38118 Modified: py/trunk/py/doc/test.txt Log: there is no gurantee anymore that tests run in order Modified: py/trunk/py/doc/test.txt ============================================================================== --- py/trunk/py/doc/test.txt (original) +++ py/trunk/py/doc/test.txt Wed Feb 7 21:48:49 2007 @@ -183,18 +183,13 @@ ``--nocapture`` option to the ``py.test`` tool. Any output will in this case be displayed as soon as it is generated. -order of execution is guaranteed +test execution order -------------------------------- -Tests will run in the order in which they appear in the files. -If you invoke ``py.test`` multiple times you should find that tests -execute in exactly the same order within each file. - -Besides making it easier to compare test output this allows -multi-stage tests where you can rely on your test to iteratively -build up a test structure. - -`Note: This is not true for distributed tests` +Tests usually run in the order in which they appear in the files. +However, tests should not rely on running one after another, as +this prevents more advanced usages: running tests +distributedly or selectively, or in "looponfailing" mode. useful tracebacks, recursion detection -------------------------------------- From hpk at codespeak.net Wed Feb 7 21:52:13 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 7 Feb 2007 21:52:13 +0100 (CET) Subject: [py-svn] r38119 - py/trunk/py/path/testing Message-ID: <20070207205213.05BE310061@code0.codespeak.net> Author: hpk Date: Wed Feb 7 21:52:09 2007 New Revision: 38119 Modified: py/trunk/py/path/testing/fscommon.py Log: avoid clashing the sample* prefix which is used by a listdir() test Modified: py/trunk/py/path/testing/fscommon.py ============================================================================== --- py/trunk/py/path/testing/fscommon.py (original) +++ py/trunk/py/path/testing/fscommon.py Wed Feb 7 21:52:09 2007 @@ -203,7 +203,7 @@ def test_move_file(self): p = self.root.join('samplefile') - newp = p.dirpath('samplefile_moved') + newp = p.dirpath('moved_samplefile') p.move(newp) assert newp.check(file=1) assert not p.check() From fijal at codespeak.net Wed Feb 7 21:54:03 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Wed, 7 Feb 2007 21:54:03 +0100 (CET) Subject: [py-svn] r38120 - py/trunk/py/rest/testing Message-ID: <20070207205403.A200D10061@code0.codespeak.net> Author: fijal Date: Wed Feb 7 21:53:57 2007 New Revision: 38120 Modified: py/trunk/py/rest/testing/test_rst2pdf.py Log: Make sure that those test run on same machine when run. (This is broader issue - we probably need to do some exclusive lock on files given to process_rest_file and such) Modified: py/trunk/py/rest/testing/test_rst2pdf.py ============================================================================== --- py/trunk/py/rest/testing/test_rst2pdf.py (original) +++ py/trunk/py/rest/testing/test_rst2pdf.py Wed Feb 7 21:53:57 2007 @@ -13,36 +13,44 @@ not py.path.local.sysfind("dot") or \ not py.path.local.sysfind("latex"): py.test.skip("ghostscript, graphviz and latex needed") + mod.data = py.magic.autopath().dirpath().join("data") -data = py.magic.autopath().dirpath().join("data") +class TestRst2Pdf(object): + def _process_rest_file(self): + data = py.magic.autopath().dirpath().join("data") + part2 = data.join("part1.txt") + pdf = part2.new(ext="pdf") + process_rest_file(part2) + assert pdf.check() + pdf.remove() -def test_process_rest_file(): - part2 = data.join("part1.txt") - pdf = part2.new(ext="pdf") - process_rest_file(part2) - assert pdf.check() - pdf.remove() + def _process_configfile(self): + data = py.magic.autopath().dirpath().join("data") + config = data.join("example.rst2pdfconfig") + pdf = config.new(ext="pdf") + tex = data.join('example.tex') + process_configfile(config, debug=True) + assert pdf.check() + assert tex.check() + texcontent = tex.read() + assert "Generated by" in texcontent + assert "Docutils" in texcontent + process_configfile(config, debug=False) + assert pdf.check() + assert not tex.check() + pdf.remove() -def test_process_configfile(): - config = data.join("example.rst2pdfconfig") - pdf = config.new(ext="pdf") - tex = data.join('example.tex') - process_configfile(config, debug=True) - assert pdf.check() - assert tex.check() - texcontent = tex.read() - assert "Generated by" in texcontent - assert "Docutils" in texcontent - process_configfile(config, debug=False) - assert pdf.check() - assert not tex.check() - pdf.remove() + def _process_all(self): + # fallback test: only checks that no exception is raised + def rec(p): + return p.check(dotfile=0) -def test_process_all(): - # fallback test: only checks that no exception is raised - def rec(p): - return p.check(dotfile=0) - for x in data.visit("*.txt", rec=rec): - yield process_rest_file, x - for x in data.visit("*.rst2pdfconfig", rec=rec): - yield process_configfile, x + for x in data.visit("*.rst2pdfconfig", rec=rec): + process_configfile(x) + for x in data.visit("*.txt", rec=rec): + process_rest_file(x) + + def test_rst2pdf(self): + self._process_rest_file() + self._process_configfile() + self._process_all() From fijal at codespeak.net Wed Feb 7 21:57:04 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Wed, 7 Feb 2007 21:57:04 +0100 (CET) Subject: [py-svn] r38121 - py/trunk/py/rest/testing Message-ID: <20070207205704.3653610061@code0.codespeak.net> Author: fijal Date: Wed Feb 7 21:56:51 2007 New Revision: 38121 Modified: py/trunk/py/rest/testing/test_directive.py Log: Same issue. Race conditions in file write. Probably we should not use the same file output for multiple tests (or to put data directory outside pylib itself). Modified: py/trunk/py/rest/testing/test_directive.py ============================================================================== --- py/trunk/py/rest/testing/test_directive.py (original) +++ py/trunk/py/rest/testing/test_directive.py Wed Feb 7 21:56:51 2007 @@ -11,36 +11,41 @@ datadir = py.magic.autopath().dirpath().join("data") testdir = py.test.ensuretemp("rest") -def test_graphviz_html(): - if not py.path.local.sysfind("dot"): - py.test.skip("graphviz needed") - directive.set_backend_and_register_directives("html") - if not py.path.local.sysfind("svn"): - py.test.skip("svn needed") - txt = datadir.join("graphviz.txt") - html = txt.new(ext="html") - png = datadir.join("example1.png") - rest.process(txt) - assert html.check() - assert png.check() - html_content = html.read() - assert png.basename in html_content - html.remove() - png.remove() +class TestGraphviz(object): + def _graphviz_html(self): + if not py.path.local.sysfind("dot"): + py.test.skip("graphviz needed") + directive.set_backend_and_register_directives("html") + if not py.path.local.sysfind("svn"): + py.test.skip("svn needed") + txt = datadir.join("graphviz.txt") + html = txt.new(ext="html") + png = datadir.join("example1.png") + rest.process(txt) + assert html.check() + assert png.check() + html_content = html.read() + assert png.basename in html_content + html.remove() + png.remove() + + def _graphviz_pdf(self): + if not py.path.local.sysfind("dot") or not py.path.local.sysfind("latex"): + py.test.skip("graphviz and latex needed") -def test_graphviz_pdf(): - if not py.path.local.sysfind("dot") or not py.path.local.sysfind("latex"): - py.test.skip("graphviz and latex needed") + directive.set_backend_and_register_directives("latex") + txt = py.path.local(datadir.join("graphviz.txt")) + pdf = txt.new(ext="pdf") + dotpdf = datadir.join("example1.pdf") + process_rest_file(txt) + assert pdf.check() + assert dotpdf.check() + pdf.remove() + dotpdf.remove() - directive.set_backend_and_register_directives("latex") - txt = py.path.local(datadir.join("graphviz.txt")) - pdf = txt.new(ext="pdf") - dotpdf = datadir.join("example1.pdf") - process_rest_file(txt) - assert pdf.check() - assert dotpdf.check() - pdf.remove() - dotpdf.remove() + def test_graphviz(self): + self._graphviz_html() + self._graphviz_pdf() def test_own_links(): def callback(name, text): From hpk at codespeak.net Wed Feb 7 22:31:16 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 7 Feb 2007 22:31:16 +0100 (CET) Subject: [py-svn] r38122 - py/trunk/py/doc Message-ID: <20070207213116.0AD9E10081@code0.codespeak.net> Author: hpk Date: Wed Feb 7 22:31:13 2007 New Revision: 38122 Modified: py/trunk/py/doc/conftest.py py/trunk/py/doc/test_conftest.py Log: fix parsing of extra lines (to be executed) Modified: py/trunk/py/doc/conftest.py ============================================================================== --- py/trunk/py/doc/conftest.py (original) +++ py/trunk/py/doc/conftest.py Wed Feb 7 22:31:13 2007 @@ -74,6 +74,7 @@ prefix = '.. >>> ' mod = py.std.types.ModuleType(self.fspath.purebasename) for line in s.split('\n'): + line = line.strip() if line.startswith(prefix): exec py.code.Source(line[len(prefix):]).compile() in \ mod.__dict__ Modified: py/trunk/py/doc/test_conftest.py ============================================================================== --- py/trunk/py/doc/test_conftest.py (original) +++ py/trunk/py/doc/test_conftest.py Wed Feb 7 22:31:13 2007 @@ -5,6 +5,21 @@ def setup_module(mod): mod.tmpdir = py.test.ensuretemp('docdoctest') +def test_doctest_extra_exec(): + # XXX get rid of the next line: + py.magic.autopath().dirpath('conftest.py').copy(tmpdir.join('conftest.py')) + xtxt = tmpdir.join('y.txt') + xtxt.write(py.code.Source(""" + hello:: + .. >>> raise ValueError + >>> None + """)) + config = py.test.config._reparse([xtxt]) + session = config.initsession() + session.main() + l = session.getitemoutcomepairs(Failed) + assert len(l) == 1 + def test_doctest_basic(): # XXX get rid of the next line: py.magic.autopath().dirpath('conftest.py').copy(tmpdir.join('conftest.py')) From hpk at codespeak.net Wed Feb 7 22:32:31 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 7 Feb 2007 22:32:31 +0100 (CET) Subject: [py-svn] r38123 - py/trunk/py/doc Message-ID: <20070207213231.A8E6310081@code0.codespeak.net> Author: hpk Date: Wed Feb 7 22:32:30 2007 New Revision: 38123 Modified: py/trunk/py/doc/conftest.py py/trunk/py/doc/path.txt Log: skip path.txt svn tests if not run via --checkremote Modified: py/trunk/py/doc/conftest.py ============================================================================== --- py/trunk/py/doc/conftest.py (original) +++ py/trunk/py/doc/conftest.py Wed Feb 7 22:32:30 2007 @@ -6,8 +6,8 @@ option = py.test.config.addoptions("documentation check options", Option('-R', '--checkremote', action="store_true", dest="checkremote", default=False, - help="check remote links in ReST files" - ), + help="perform tests involving remote accesses (links, svn)" + ), Option('', '--forcegen', action="store_true", dest="forcegen", default=False, help="force generation of html files even if they appear up-to-date" Modified: py/trunk/py/doc/path.txt ============================================================================== --- py/trunk/py/doc/path.txt (original) +++ py/trunk/py/doc/path.txt Wed Feb 7 22:32:30 2007 @@ -51,6 +51,8 @@ Some example usage of :api:`py.path.svnurl`:: + .. >>> import py + .. >>> if not getattr(py.test.config.option, 'checkremote', 0): py.test.skip("use --checkremote to enable svn remote doctests") >>> url = py.path.svnurl('http://codespeak.net/svn/py') >>> info = url.info() >>> info.kind From fijal at codespeak.net Wed Feb 7 22:35:15 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Wed, 7 Feb 2007 22:35:15 +0100 (CET) Subject: [py-svn] r38124 - py/trunk/py/test/rsession Message-ID: <20070207213515.D89E81007F@code0.codespeak.net> Author: fijal Date: Wed Feb 7 22:35:14 2007 New Revision: 38124 Modified: py/trunk/py/test/rsession/executor.py Log: No, this is needed for C-c to work. Modified: py/trunk/py/test/rsession/executor.py ============================================================================== --- py/trunk/py/test/rsession/executor.py (original) +++ py/trunk/py/test/rsession/executor.py Wed Feb 7 22:35:14 2007 @@ -29,6 +29,8 @@ outcome = Outcome() except Skipped, e: outcome = Outcome(skipped=str(e)) + except (SystemExit, KeyboardInterrupt): + raise except: excinfo = py.code.ExceptionInfo() if isinstance(self.item, py.test.Function): From hpk at codespeak.net Wed Feb 7 22:56:17 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 7 Feb 2007 22:56:17 +0100 (CET) Subject: [py-svn] r38125 - in py/trunk/py/test: rsession testing Message-ID: <20070207215617.5FD2F1007B@code0.codespeak.net> Author: hpk Date: Wed Feb 7 22:56:14 2007 New Revision: 38125 Modified: py/trunk/py/test/rsession/rsession.py py/trunk/py/test/testing/test_config.py Log: run unboxed by default, split/improve tests for it Modified: py/trunk/py/test/rsession/rsession.py ============================================================================== --- py/trunk/py/test/rsession/rsession.py (original) +++ py/trunk/py/test/rsession/rsession.py Wed Feb 7 22:56:14 2007 @@ -113,7 +113,6 @@ print "Cannot use nocapture with distributed testing" sys.exit(1) config = self.config - config.option.boxed = True try: config.getvalue('dist_hosts') except KeyError: Modified: py/trunk/py/test/testing/test_config.py ============================================================================== --- py/trunk/py/test/testing/test_config.py (original) +++ py/trunk/py/test/testing/test_config.py Wed Feb 7 22:56:14 2007 @@ -241,7 +241,7 @@ session = config.initsession() assert session.config is config - def test_boxed_option_including_implied_from_conftest(self): + def test_boxed_option_default(self): self.tmpdir.join("conftest.py").write("dist_hosts=[]") tmpdir = self.tmpdir.ensure("subdir", dir=1) config = py.test.config._reparse([tmpdir]) @@ -249,8 +249,11 @@ assert not config.option.boxed config = py.test.config._reparse(['--dist', tmpdir]) config.initsession() - assert config.option.boxed + assert not config.option.boxed + def test_boxed_option_from_conftest(self): + self.tmpdir.join("conftest.py").write("dist_hosts=[]") + tmpdir = self.tmpdir.ensure("subdir", dir=1) tmpdir.join("conftest.py").write(py.code.Source(""" dist_hosts = [] dist_boxed = True @@ -258,6 +261,9 @@ config = py.test.config._reparse(['--dist', tmpdir]) config.initsession() assert config.option.boxed + + def test_boxed_option_from_conftest2(self): + tmpdir = self.tmpdir tmpdir.join("conftest.py").write(py.code.Source(""" dist_boxed = False """)) @@ -265,11 +271,9 @@ assert config.option.boxed config.initsession() assert config.option.boxed - config = py.test.config._reparse([tmpdir, '-d']) - assert not config.option.boxed - config.initsession() - assert config.option.boxed - config = py.test.config._reparse([tmpdir, '-d', '-s']) + + def test_dist_session_no_capturedisable(self): + config = py.test.config._reparse([self.tmpdir, '-d', '-s']) py.test.raises(SystemExit, "config.initsession()") def test_getvalue_pathlist(self): From guido at codespeak.net Thu Feb 8 13:26:04 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Thu, 8 Feb 2007 13:26:04 +0100 (CET) Subject: [py-svn] r38140 - in py/trunk/py/rest: . testing Message-ID: <20070208122604.A904A10083@code0.codespeak.net> Author: guido Date: Thu Feb 8 13:26:02 2007 New Revision: 38140 Modified: py/trunk/py/rest/rst.py py/trunk/py/rest/testing/test_rst.py Log: Empty literal blocks are not allowed: removing them. Modified: py/trunk/py/rest/rst.py ============================================================================== --- py/trunk/py/rest/rst.py (original) +++ py/trunk/py/rest/rst.py Thu Feb 8 13:26:02 2007 @@ -133,7 +133,8 @@ for child in self.children: outcome.append(child.text()) - text = self.sep.join(outcome) + "\n" # trailing newline + # always a trailing newline + text = self.sep.join([i for i in outcome if i]) + "\n" return text + self.render_links() class Transition(AbstractNode): @@ -251,6 +252,8 @@ start = '::\n\n' def text(self): + if not self._text.strip(): + return '' text = self.escape(self._text).split('\n') for i, line in py.builtin.enumerate(text): if line.strip(): Modified: py/trunk/py/rest/testing/test_rst.py ============================================================================== --- py/trunk/py/rest/testing/test_rst.py (original) +++ py/trunk/py/rest/testing/test_rst.py Thu Feb 8 13:26:02 2007 @@ -175,6 +175,17 @@ assert txt == expected checkrest(txt) +def test_blockquote_empty(): + expected = """\ +Foo + +Bar +""" + txt = Rest(Paragraph('Foo'), LiteralBlock(''), Paragraph('Bar')).text() + print repr(txt) + assert txt == expected + checkrest(txt) + def test_title(): txt = Title(Text("Some title"), belowchar="=").text() assert txt == "Some title\n==========" From guido at codespeak.net Thu Feb 8 14:47:25 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Thu, 8 Feb 2007 14:47:25 +0100 (CET) Subject: [py-svn] r38142 - py/trunk/py/doc Message-ID: <20070208134725.8A4961007B@code0.codespeak.net> Author: guido Date: Thu Feb 8 14:47:23 2007 New Revision: 38142 Modified: py/trunk/py/doc/conftest.py py/trunk/py/doc/test_conftest.py Log: Fixed problem with indentation in the results of a doctest. Modified: py/trunk/py/doc/conftest.py ============================================================================== --- py/trunk/py/doc/conftest.py (original) +++ py/trunk/py/doc/conftest.py Thu Feb 8 14:47:23 2007 @@ -14,6 +14,24 @@ ) ) +def deindent(s, sep='\n'): + leastspaces = -1 + lines = s.split(sep) + for line in lines: + if not line.strip(): + continue + spaces = len(line) - len(line.lstrip()) + if leastspaces == -1 or spaces < leastspaces: + leastspaces = spaces + if leastspaces == -1: + return s + for i, line in py.builtin.enumerate(lines): + if not line.strip(): + lines[i] = '' + else: + lines[i] = line[leastspaces:] + return sep.join(lines) + _initialized = False def checkdocutils(): global _initialized @@ -73,10 +91,10 @@ l = [] prefix = '.. >>> ' mod = py.std.types.ModuleType(self.fspath.purebasename) - for line in s.split('\n'): - line = line.strip() - if line.startswith(prefix): - exec py.code.Source(line[len(prefix):]).compile() in \ + for line in deindent(s).split('\n'): + stripped = line.strip() + if stripped.startswith(prefix): + exec py.code.Source(stripped[len(prefix):]).compile() in \ mod.__dict__ line = "" else: Modified: py/trunk/py/doc/test_conftest.py ============================================================================== --- py/trunk/py/doc/test_conftest.py (original) +++ py/trunk/py/doc/test_conftest.py Thu Feb 8 14:47:23 2007 @@ -52,6 +52,15 @@ l2 = session.getitemoutcomepairs(Skipped) assert len(l+l2) == 2 +def test_deindent(): + from py.__.doc.conftest import deindent + assert deindent('foo') == 'foo' + assert deindent('foo\n bar') == 'foo\n bar' + assert deindent(' foo\n bar\n') == 'foo\nbar\n' + assert deindent(' foo\n\n bar\n') == 'foo\n\nbar\n' + assert deindent(' foo\n bar\n') == 'foo\n bar\n' + assert deindent(' foo\n bar\n') == ' foo\nbar\n' + def test_doctest_eol(): # XXX get rid of the next line: py.magic.autopath().dirpath('conftest.py').copy(tmpdir.join('conftest.py')) @@ -67,6 +76,21 @@ l2 = session.getitemoutcomepairs(Skipped) assert len(l+l2) == 2 +def test_doctest_indentation(): + # XXX get rid of the next line: + py.magic.autopath().dirpath('conftest.py').copy(tmpdir.join('conftest.py')) + + txt = tmpdir.join('foo.txt') + txt.write('..\n >>> print "foo\\n bar"\n foo\n bar\n') + config = py.test.config._reparse([txt]) + session = config.initsession() + session.main() + l = session.getitemoutcomepairs(Failed) + assert len(l) == 0 + l = session.getitemoutcomepairs(Passed) + l2 = session.getitemoutcomepairs(Skipped) + assert len(l+l2) == 2 + def test_js_ignore(): py.magic.autopath().dirpath('conftest.py').copy(tmpdir.join('conftest.py')) tmpdir.ensure('__init__.py') From guido at codespeak.net Thu Feb 8 14:51:38 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Thu, 8 Feb 2007 14:51:38 +0100 (CET) Subject: [py-svn] r38143 - py/trunk/py/misc/testing Message-ID: <20070208135138.074E91008A@code0.codespeak.net> Author: guido Date: Thu Feb 8 14:51:38 2007 New Revision: 38143 Modified: py/trunk/py/misc/testing/test_update_website.py Log: Removed some confusing output to stdout/stderr (output of failing tests, which are part of a test that tests whether a set of tests ran by a nested py.test process fails... so the failing output is correct). Modified: py/trunk/py/misc/testing/test_update_website.py ============================================================================== --- py/trunk/py/misc/testing/test_update_website.py (original) +++ py/trunk/py/misc/testing/test_update_website.py Thu Feb 8 14:51:38 2007 @@ -1,4 +1,5 @@ import py +import sys here = py.magic.autopath().dirpath() update_website = here.join('../../bin/_update_website.py').pyimport() @@ -53,9 +54,11 @@ assert pkgpath.join('../apigen/api/sub.foo.html').check(file=True) def test_run_tests_failure(): + if py.std.sys.platform == "win32": + py.test.skip("update_website is not supposed to be run from win32") pkgpath = setup_pkg('update_website_run_tests_failure') assert not pkgpath.join('../apigen').check(dir=True) pkgpath.ensure('../apigen', file=True) - errors = update_website.run_tests(pkgpath) + errors = update_website.run_tests(pkgpath, '> /dev/null 2>&1') assert errors # some error message From guido at codespeak.net Thu Feb 8 14:54:39 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Thu, 8 Feb 2007 14:54:39 +0100 (CET) Subject: [py-svn] r38144 - py/trunk/py/apigen Message-ID: <20070208135439.46D2B1008D@code0.codespeak.net> Author: guido Date: Thu Feb 8 14:54:38 2007 New Revision: 38144 Modified: py/trunk/py/apigen/html.py py/trunk/py/apigen/linker.py py/trunk/py/apigen/style.css Log: Fixed a problem with the linker which on building api/source docs found matches in its own source code, moved some elements in the apigen docs out of a header where they shouldn't have been (making the font too large). Modified: py/trunk/py/apigen/html.py ============================================================================== --- py/trunk/py/apigen/html.py (original) +++ py/trunk/py/apigen/html.py Thu Feb 8 14:54:38 2007 @@ -22,16 +22,17 @@ class ClassDescription(Description): pass - class ClassDef(html.h1): + class ClassDef(html.div): def __init__(self, classname, bases, docstring, sourcelink, properties, methods): - super(H.ClassDef, self).__init__('class %s(' % classname,) + header = H.h1('class %s(' % (classname,)) for name, href in bases: link = name if href is not None: link = H.a(name, href=href) - self.append(H.BaseDescription(link)) - self.append('):') + header.append(H.BaseDescription(link)) + header.append('):') + super(H.ClassDef, self).__init__(header) self.append(H.div(H.Docstring(docstring or '*no docstring available*'), sourcelink, Modified: py/trunk/py/apigen/linker.py ============================================================================== --- py/trunk/py/apigen/linker.py (original) +++ py/trunk/py/apigen/linker.py Thu Feb 8 14:54:38 2007 @@ -2,6 +2,12 @@ import os html = py.xml.html +# this here to serve two functions: first it makes the proto part of the temp +# urls (see TempLinker) customizable easily (for tests and such) and second +# it makes sure the temp links aren't replaced in generated source code etc. +# for this file (and its tests) itself. +TEMPLINK_PROTO = 'apigen.temp' + def getrelfspath(dotted_name): # XXX need to make sure its imported on non-py lib return eval(dotted_name, {"py": py}) @@ -58,20 +64,22 @@ self._linkid2target = {} def get_lazyhref(self, linkid): - return 'apigen.linker://%s' % (linkid,) + return '%s://%s' % (TEMPLINK_PROTO, linkid) def set_link(self, linkid, target): assert linkid not in self._linkid2target self._linkid2target[linkid] = target def get_target(self, tempurl, fromlocation=None): + assert tempurl.startswith('%s://' % (TEMPLINK_PROTO,)) linkid = '://'.join(tempurl.split('://')[1:]) linktarget = self._linkid2target[linkid] if fromlocation is not None: linktarget = relpath(fromlocation, linktarget) return linktarget - _reg_tempurl = py.std.re.compile('"(apigen.linker:\/\/[^"\s]*)"') + _reg_tempurl = py.std.re.compile('["\'](%s:\/\/[^"\s]*)["\']' % ( + TEMPLINK_PROTO,)) def replace_dirpath(self, dirpath, stoponerrors=True): """ replace temporary links in all html files in dirpath and below """ for fpath in dirpath.visit('*.html'): Modified: py/trunk/py/apigen/style.css ============================================================================== --- py/trunk/py/apigen/style.css (original) +++ py/trunk/py/apigen/style.css Thu Feb 8 14:54:38 2007 @@ -34,10 +34,6 @@ list-style-type: none; } -.classdoc { - font-size: 0.8em; -} - .code a { color: blue; font-weight: bold; From guido at codespeak.net Thu Feb 8 15:52:51 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Thu, 8 Feb 2007 15:52:51 +0100 (CET) Subject: [py-svn] r38148 - in py/trunk/py/apigen: . testing Message-ID: <20070208145251.9E6BE10087@code0.codespeak.net> Author: guido Date: Thu Feb 8 15:52:49 2007 New Revision: 38148 Modified: py/trunk/py/apigen/html.py py/trunk/py/apigen/htmlgen.py py/trunk/py/apigen/testing/test_apigen_functional.py Log: Made that stacks are built on seperate pages instead of inline in the function information, to avoid having > 5MB pages... Modified: py/trunk/py/apigen/html.py ============================================================================== --- py/trunk/py/apigen/html.py (original) +++ py/trunk/py/apigen/html.py Thu Feb 8 15:52:49 2007 @@ -160,15 +160,13 @@ pass class CallStackDescription(Description): - def __init__(self, callstackdiv): - super(H.CallStackDescription, self).__init__( - H.Hideable('callsites', 'callsites', csdiv)) + pass - class CallStackItem(html.div): - def __init__(self, filename, lineno, traceback): - super(H.CallStackItem, self).__init__( - H.Hideable("stack trace %s - line %s" % (filename, lineno), - 'callstackitem', traceback)) + class CallStackLink(html.div): + def __init__(self, filename, lineno, href): + super(H.CallStackLink, self).__init__( + H.a("stack trace %s - line %s" % (filename, lineno), + href=href)) class Hideable(html.div): def __init__(self, name, class_, *content): Modified: py/trunk/py/apigen/htmlgen.py ============================================================================== --- py/trunk/py/apigen/htmlgen.py (original) +++ py/trunk/py/apigen/htmlgen.py Thu Feb 8 15:52:49 2007 @@ -341,13 +341,9 @@ href = self.linker.get_lazyhref(sourcefile) csource = H.SourceSnippet(text, href, colored) - callstack = self.dsa.get_function_callpoints(dotted_name) - csitems = [] - for cs, _ in callstack: - csitems.append(self.build_callsite(dotted_name, cs)) + cslinks = self.build_callsites(dotted_name) snippet = H.FunctionDescription(localname, argdesc, docstring, - valuedesc, csource, csitems) - + valuedesc, csource, cslinks) return snippet def build_class_view(self, dotted_name): @@ -589,14 +585,30 @@ def is_in_pkg(self, sourcefile): return py.path.local(sourcefile).relto(self.projpath) - def build_callsite(self, functionname, call_site): - tbtag = self.gen_traceback(functionname, reversed(call_site)) - return H.CallStackItem(call_site[0].filename, call_site[0].lineno + 1, - tbtag) + def build_callsites(self, dotted_name): + callstack = self.dsa.get_function_callpoints(dotted_name) + cslinks = [] + for i, (cs, _) in enumerate(callstack): + link = self.build_callsite(dotted_name, cs, i) + cslinks.append(link) + return cslinks + + def build_callsite(self, dotted_name, call_site, index): + tbtag = self.gen_traceback(dotted_name, reversed(call_site)) + parent_dotted_name, _ = split_of_last_part(dotted_name) + nav = self.build_navigation(parent_dotted_name, False) + id = 'callsite_%s_%s' % (dotted_name, index) + reltargetpath = "api/%s.html" % (id,) + self.linker.set_link(id, reltargetpath) + href = self.linker.get_lazyhref(id) + self.write_page('call site %s for %s' % (index, dotted_name), + reltargetpath, tbtag, nav) + return H.CallStackLink(call_site[0].filename, call_site[0].lineno + 1, + href) _reg_source = py.std.re.compile(r'([^>]*)<(.*)>') - def gen_traceback(self, funcname, call_site): - tbdiv = H.div() + def gen_traceback(self, dotted_name, call_site): + tbtag = H.CallStackDescription() for frame in call_site: lineno = frame.lineno - frame.firstlineno source = frame.source @@ -631,7 +643,7 @@ else: sourcelink = H.div('source unknown (%s)' % (sourcefile,)) colored = mangled[:] - tbdiv.append(sourcelink) - tbdiv.append(H.div(*colored)) - return tbdiv + tbtag.append(sourcelink) + tbtag.append(H.div(*colored)) + return tbtag Modified: py/trunk/py/apigen/testing/test_apigen_functional.py ============================================================================== --- py/trunk/py/apigen/testing/test_apigen_functional.py (original) +++ py/trunk/py/apigen/testing/test_apigen_functional.py Thu Feb 8 15:52:49 2007 @@ -100,6 +100,11 @@ exec c in globals() assert pak.somenamespace._hidden() == 'quux' + + # this just to see a multi-level stack in the docs + def foo(): + return pak.main.sub.func(10) + assert foo() is None """)) return temp, 'pak' From guido at codespeak.net Thu Feb 8 16:14:22 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Thu, 8 Feb 2007 16:14:22 +0100 (CET) Subject: [py-svn] r38150 - py/trunk/py/apigen/testing Message-ID: <20070208151422.03E2410094@code0.codespeak.net> Author: guido Date: Thu Feb 8 16:14:21 2007 New Revision: 38150 Modified: py/trunk/py/apigen/testing/test_apigen_example.py Log: Fixed test that failed after the last change in apigen (removing the stacks from the callable info boxes). Modified: py/trunk/py/apigen/testing/test_apigen_example.py ============================================================================== --- py/trunk/py/apigen/testing/test_apigen_example.py (original) +++ py/trunk/py/apigen/testing/test_apigen_example.py Thu Feb 8 16:14:21 2007 @@ -126,7 +126,7 @@ pkg.main.sub.func(pkg.main.SomeClass(10)) t.end_tracing() apb = ApiPageBuilder(self.base, self.linker, dsa, self.fs_root, - self.namespace_tree, 'root docstring') + self.namespace_tree, self.project) snippet = apb.build_callable_view('main.sub.func') html = snippet.unicode() print html @@ -147,8 +147,6 @@ pos7 = html.find('source: %s' % (self.fs_root.join('pkg/func.py'),), pos6) assert pos7 > pos6 - pos8 = html.find('def func(arg1):', pos7) - assert pos8 > pos7 _checkhtmlsnippet(html) def test_build_function_pages(self): From guido at codespeak.net Thu Feb 8 16:14:53 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Thu, 8 Feb 2007 16:14:53 +0100 (CET) Subject: [py-svn] r38151 - py/trunk/py/doc Message-ID: <20070208151453.8FAFC10095@code0.codespeak.net> Author: guido Date: Thu Feb 8 16:14:52 2007 New Revision: 38151 Modified: py/trunk/py/doc/test.txt Log: Small change in the text to make it a bit more understandable. Modified: py/trunk/py/doc/test.txt ============================================================================== --- py/trunk/py/doc/test.txt (original) +++ py/trunk/py/doc/test.txt Thu Feb 8 16:14:52 2007 @@ -189,7 +189,8 @@ Tests usually run in the order in which they appear in the files. However, tests should not rely on running one after another, as this prevents more advanced usages: running tests -distributedly or selectively, or in "looponfailing" mode. +distributedly or selectively, or in "looponfailing" mode, +will cause them to run in random order. useful tracebacks, recursion detection -------------------------------------- From fijal at codespeak.net Thu Feb 8 16:31:41 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Thu, 8 Feb 2007 16:31:41 +0100 (CET) Subject: [py-svn] r38164 - in py/trunk/py: execnet execnet/testing test/rsession test/rsession/testing Message-ID: <20070208153141.6A16410088@code0.codespeak.net> Author: fijal Date: Thu Feb 8 16:31:38 2007 New Revision: 38164 Modified: py/trunk/py/execnet/rsync.py py/trunk/py/execnet/testing/test_rsync.py py/trunk/py/test/rsession/hostmanage.py py/trunk/py/test/rsession/testing/test_hostmanage.py Log: Move source as an rsync initialisation argument rather than as a send parameter Modified: py/trunk/py/execnet/rsync.py ============================================================================== --- py/trunk/py/execnet/rsync.py (original) +++ py/trunk/py/execnet/rsync.py Thu Feb 8 16:31:38 2007 @@ -15,9 +15,10 @@ symlinks will be just copied (regardless of existance of such a path on remote side). """ - def __init__(self, callback=None, verbose=True, **options): + def __init__(self, sourcedir, callback=None, verbose=True, **options): for name in options: assert name in ('delete') + self._sourcedir = str(sourcedir) self._options = options self._verbose = verbose assert callback is None or callable(callback) @@ -92,13 +93,12 @@ if self._verbose: print '%s <= %s' % (gateway.remoteaddress, modified_rel_path) - def send(self, sourcedir): + def send(self): """ Sends a sourcedir to all added targets. """ if not self._channels: raise IOError("no targets available, maybe you " "are trying call send() twice?") - self._sourcedir = str(sourcedir) # normalize a trailing '/' away self._sourcedir = os.path.dirname(os.path.join(self._sourcedir, 'x')) # send directory structure and file timestamps/sizes Modified: py/trunk/py/execnet/testing/test_rsync.py ============================================================================== --- py/trunk/py/execnet/testing/test_rsync.py (original) +++ py/trunk/py/execnet/testing/test_rsync.py Thu Feb 8 16:31:38 2007 @@ -21,8 +21,8 @@ class TestRSync(DirSetup): def test_notargets(self): - rsync = RSync() - py.test.raises(IOError, "rsync.send(self.source)") + rsync = RSync(self.source) + py.test.raises(IOError, "rsync.send()") def test_dirsync(self): dest = self.dest1 @@ -31,10 +31,10 @@ for s in ('content1', 'content2-a-bit-longer'): source.ensure('subdir', 'file1').write(s) - rsync = RSync() + rsync = RSync(self.source) rsync.add_target(gw, dest) rsync.add_target(gw2, dest2) - rsync.send(source) + rsync.send() assert dest.join('subdir').check(dir=1) assert dest.join('subdir', 'file1').check(file=1) assert dest.join('subdir', 'file1').read() == s @@ -43,40 +43,40 @@ assert dest2.join('subdir', 'file1').read() == s source.join('subdir').remove('file1') - rsync = RSync() + rsync = RSync(source) rsync.add_target(gw2, dest2) rsync.add_target(gw, dest) - rsync.send(source) + rsync.send() assert dest.join('subdir', 'file1').check(file=1) assert dest2.join('subdir', 'file1').check(file=1) - rsync = RSync(delete=True) + rsync = RSync(source, delete=True) rsync.add_target(gw2, dest2) rsync.add_target(gw, dest) - rsync.send(source) + rsync.send() assert not dest.join('subdir', 'file1').check() assert not dest2.join('subdir', 'file1').check() def test_dirsync_twice(self): source = self.source source.ensure("hello") - rsync = RSync() + rsync = RSync(source) rsync.add_target(gw, self.dest1) - rsync.send(self.source) + rsync.send() assert self.dest1.join('hello').check() - py.test.raises(IOError, "rsync.send(self.source)") + py.test.raises(IOError, "rsync.send()") rsync.add_target(gw, self.dest2) - rsync.send(self.source) + rsync.send() assert self.dest2.join('hello').check() - py.test.raises(IOError, "rsync.send(self.source)") + py.test.raises(IOError, "rsync.send()") def test_rsync_default_reporting(self): source = self.source source.ensure("hello") cap = py.io.StdCapture() try: - rsync = RSync() + rsync = RSync(source) rsync.add_target(gw, self.dest1) - rsync.send(self.source) + rsync.send() finally: out, err = cap.reset() assert out.find("hello") != -1 @@ -86,9 +86,9 @@ source.ensure("hello") cap = py.io.StdCapture() try: - rsync = RSync(verbose=False) + rsync = RSync(source, verbose=False) rsync.add_target(gw, self.dest1) - rsync.send(self.source) + rsync.send() finally: out, err = cap.reset() assert not out @@ -103,10 +103,10 @@ source.join("rellink").mksymlinkto(source.join("existant"), absolute=0) source.join('abslink').mksymlinkto(source.join("existant")) - rsync = RSync() + rsync = RSync(source) rsync.add_target(gw, dest) - rsync.send(source) - + rsync.send() + assert dest.join('rellink').readlink() == dest.join("existant") assert dest.join('abslink').readlink() == dest.join("existant") @@ -119,10 +119,10 @@ def callback(cmd, lgt, channel): total[(cmd, lgt)] = True - rsync = RSync(callback=callback) + rsync = RSync(source, callback=callback) #rsync = RSync() rsync.add_target(gw, dest) - rsync.send(source) + rsync.send() assert total == {("list", 110):True, ("ack", 100):True, ("ack", 10):True} @@ -140,9 +140,9 @@ source.join("ex2").remove() return True - rsync = DRsync() + rsync = DRsync(source) rsync.add_target(gw, dest) - rsync.send(source) + rsync.send() assert rsync.x == 1 assert len(dest.listdir()) == 1 assert len(source.listdir()) == 1 Modified: py/trunk/py/test/rsession/hostmanage.py ============================================================================== --- py/trunk/py/test/rsession/hostmanage.py (original) +++ py/trunk/py/test/rsession/hostmanage.py Thu Feb 8 16:31:38 2007 @@ -67,14 +67,14 @@ class HostRSync(py.execnet.RSync): """ RSyncer that filters out common files """ - def __init__(self, *args, **kwargs): + def __init__(self, source, *args, **kwargs): self._synced = {} ignores= None if 'ignores' in kwargs: ignores = kwargs.pop('ignores') self._ignores = ignores or [] kwargs['delete'] = True - super(HostRSync, self).__init__(**kwargs) + super(HostRSync, self).__init__(source, **kwargs) def filter(self, path): path = py.path.local(path) @@ -129,7 +129,7 @@ ignores = self.config.getvalue_pathlist("dist_rsync_ignore") self.prepare_gateways(reporter) for root in self.roots: - rsync = HostRSync(ignores=ignores, + rsync = HostRSync(root, ignores=ignores, verbose=self.config.option.verbose) destrelpath = root.relto(self.config.topdir) for host in self.hosts: @@ -139,7 +139,7 @@ host, reporter, destrelpath, finishedcallback= lambda host=host, root=root: donecallback(host, root)) reporter(repevent.HostRSyncing(host, root, remotepath)) - rsync.send(root) + rsync.send() def setup_hosts(self, reporter): self.init_rsync(reporter) @@ -170,12 +170,12 @@ def teardown_gateways(self, reporter, channels): for channel in channels: + #try: try: - repevent.wrapcall(reporter, channel.waitclose) - except KeyboardInterrupt, SystemExit: - raise - except: - pass + repevent.wrapcall(reporter, channel.waitclose, 1) + except IOError: # timeout + # force closing + channel.close() channel.gateway.exit() def gethomedir(): Modified: py/trunk/py/test/rsession/testing/test_hostmanage.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_hostmanage.py (original) +++ py/trunk/py/test/rsession/testing/test_hostmanage.py Thu Feb 8 16:31:38 2007 @@ -106,7 +106,7 @@ self.source.ensure(".svn", "entries") self.source.ensure(".somedotfile", "moreentries") self.source.ensure("somedir", "editfile~") - syncer = HostRSync() + syncer = HostRSync(self.source) l = list(self.source.visit(rec=syncer.filter, fil=syncer.filter)) assert len(l) == 3 @@ -118,18 +118,18 @@ def test_hrsync_one_host(self): h1 = self._gethostinfo() finished = [] - rsync = HostRSync() + rsync = HostRSync(self.source) h1.initgateway() rsync.add_target_host(h1) self.source.join("hello.py").write("world") - rsync.send(self.source) + rsync.send() assert self.dest.join("hello.py").check() def test_hrsync_same_host_twice(self): h1 = self._gethostinfo() h2 = self._gethostinfo() finished = [] - rsync = HostRSync() + rsync = HostRSync(self.source) l = [] h1.initgateway() res1 = rsync.add_target_host(h1) From fijal at codespeak.net Thu Feb 8 16:35:17 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Thu, 8 Feb 2007 16:35:17 +0100 (CET) Subject: [py-svn] r38165 - py/trunk/py/bin Message-ID: <20070208153517.5650810088@code0.codespeak.net> Author: fijal Date: Thu Feb 8 16:35:11 2007 New Revision: 38165 Modified: py/trunk/py/bin/_update_website.py Log: Update this as well Modified: py/trunk/py/bin/_update_website.py ============================================================================== --- py/trunk/py/bin/_update_website.py (original) +++ py/trunk/py/bin/_update_website.py Thu Feb 8 16:35:11 2007 @@ -18,9 +18,9 @@ pkgpath.copy(tempdir.ensure(pkgpath.basename, dir=True)) apidocspath.copy(tempdir.ensure(apidocspath.basename, dir=True)) - rs = py.execnet.RSync(delete=True) + rs = py.execnet.RSync(tempdir, delete=True) rs.add_target(gateway, remotepath) - rs.send(tempdir) + rs.send() def run_tests(pkgpath, args=''): """ run the unit tests and build the docs """ From fijal at codespeak.net Thu Feb 8 16:54:54 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Thu, 8 Feb 2007 16:54:54 +0100 (CET) Subject: [py-svn] r38172 - py/trunk/py/execnet Message-ID: <20070208155454.8005E100A2@code0.codespeak.net> Author: fijal Date: Thu Feb 8 16:54:53 2007 New Revision: 38172 Modified: py/trunk/py/execnet/rsync.py Log: Add a method which sends only if there are available targets Modified: py/trunk/py/execnet/rsync.py ============================================================================== --- py/trunk/py/execnet/rsync.py (original) +++ py/trunk/py/execnet/rsync.py Thu Feb 8 16:54:53 2007 @@ -93,6 +93,13 @@ if self._verbose: print '%s <= %s' % (gateway.remoteaddress, modified_rel_path) + def send_if_targets(self): + """ Sends only if there are targets, otherwise returns + """ + if not self._channels: + return + self.send() + def send(self): """ Sends a sourcedir to all added targets. """ From fijal at codespeak.net Thu Feb 8 16:56:35 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Thu, 8 Feb 2007 16:56:35 +0100 (CET) Subject: [py-svn] r38173 - in py/trunk/py/test/rsession: . testing Message-ID: <20070208155635.1AF66100A4@code0.codespeak.net> Author: fijal Date: Thu Feb 8 16:56:33 2007 New Revision: 38173 Modified: py/trunk/py/test/rsession/hostmanage.py py/trunk/py/test/rsession/testing/test_hostmanage.py Log: * Minor semantics change, now host and host: are the same (we don't want to rsync to home dir usually) * Make a flag rsync_flag in hostinfo which tells whether to rsync or no, semantics are that in case of localhost we do not rsync and in case of localhost: we do. Modified: py/trunk/py/test/rsession/hostmanage.py ============================================================================== --- py/trunk/py/test/rsession/hostmanage.py (original) +++ py/trunk/py/test/rsession/hostmanage.py Thu Feb 8 16:56:33 2007 @@ -17,10 +17,14 @@ def __init__(self, spec): parts = spec.split(':', 1) self.hostname = parts.pop(0) - if parts: + if parts and parts[0]: self.relpath = parts[0] else: - self.relpath = "pytestcache-" + self.hostname + self.relpath = "pytestcache-" + self.hostname + if spec.find(':') == -1 and self.hostname == 'localhost': + self.rsync_flag = False + else: + self.rsync_flag = True self.hostid = self._getuniqueid(self.hostname) def _getuniqueid(self, hostname): @@ -90,7 +94,7 @@ def add_target_host(self, host, reporter=lambda x: None, destrelpath=None, finishedcallback=None): key = host.hostname, host.relpath - if key in self._synced: + if not host.rsync_flag or key in self._synced: if finishedcallback: finishedcallback() return False @@ -139,7 +143,7 @@ host, reporter, destrelpath, finishedcallback= lambda host=host, root=root: donecallback(host, root)) reporter(repevent.HostRSyncing(host, root, remotepath)) - rsync.send() + rsync.send_if_targets() def setup_hosts(self, reporter): self.init_rsync(reporter) Modified: py/trunk/py/test/rsession/testing/test_hostmanage.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_hostmanage.py (original) +++ py/trunk/py/test/rsession/testing/test_hostmanage.py Thu Feb 8 16:56:33 2007 @@ -24,7 +24,7 @@ return hostinfo def test_defaultpath(self): - x = HostInfo("localhost") + x = HostInfo("localhost:") assert x.hostname == "localhost" assert x.relpath == "pytestcache-" + x.hostname @@ -34,11 +34,11 @@ assert x.hostname == "localhost" def test_hostid(self): - x = HostInfo("localhost") - y = HostInfo("localhost") + x = HostInfo("localhost:") + y = HostInfo("localhost:") assert x.hostid != y.hostid x = HostInfo("localhost:/tmp") - y = HostInfo("localhost") + y = HostInfo("localhost:") assert x.hostid != y.hostid def test_non_existing_hosts(self): @@ -192,6 +192,20 @@ assert self.dest.join("dir5","file").check() assert not self.dest.join("dir6").check() + def test_hostmanage_optimise_localhost(self): + def add_target(self, a, b, c): + assert 0, "Should not rsync here" + try: + config = py.test.config._reparse([self.source]) + old_add_target = HostRSync.add_target + HostRSync.add_target = add_target + hm = HostManager(config, hosts=[HostInfo('localhost') for i in + range(3)]) + events = [] + hm.init_rsync(reporter=events.append) + finally: + HostRSync.add_target = old_add_target + def test_getpath_relto_home(): x = getpath_relto_home("hello") assert x == py.path.local._gethomedir().join("hello") From fijal at codespeak.net Thu Feb 8 17:01:52 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Thu, 8 Feb 2007 17:01:52 +0100 (CET) Subject: [py-svn] r38174 - in py/trunk/py/execnet: . testing Message-ID: <20070208160152.1299510090@code0.codespeak.net> Author: fijal Date: Thu Feb 8 17:01:51 2007 New Revision: 38174 Modified: py/trunk/py/execnet/rsync.py py/trunk/py/execnet/testing/test_rsync.py Log: Flag instead of new method and a test. Modified: py/trunk/py/execnet/rsync.py ============================================================================== --- py/trunk/py/execnet/rsync.py (original) +++ py/trunk/py/execnet/rsync.py Thu Feb 8 17:01:51 2007 @@ -93,19 +93,16 @@ if self._verbose: print '%s <= %s' % (gateway.remoteaddress, modified_rel_path) - def send_if_targets(self): - """ Sends only if there are targets, otherwise returns + def send(self, raises=True): + """ Sends a sourcedir to all added targets. Flag indicates + whether to raise an error or return in case of lack of + targets """ if not self._channels: + if raises: + raise IOError("no targets available, maybe you " + "are trying call send() twice?") return - self.send() - - def send(self): - """ Sends a sourcedir to all added targets. - """ - if not self._channels: - raise IOError("no targets available, maybe you " - "are trying call send() twice?") # normalize a trailing '/' away self._sourcedir = os.path.dirname(os.path.join(self._sourcedir, 'x')) # send directory structure and file timestamps/sizes Modified: py/trunk/py/execnet/testing/test_rsync.py ============================================================================== --- py/trunk/py/execnet/testing/test_rsync.py (original) +++ py/trunk/py/execnet/testing/test_rsync.py Thu Feb 8 17:01:51 2007 @@ -23,6 +23,7 @@ def test_notargets(self): rsync = RSync(self.source) py.test.raises(IOError, "rsync.send()") + assert rsync.send(raises=False) is None def test_dirsync(self): dest = self.dest1 @@ -64,10 +65,12 @@ rsync.send() assert self.dest1.join('hello').check() py.test.raises(IOError, "rsync.send()") + assert rsync.send(raises=False) is None rsync.add_target(gw, self.dest2) rsync.send() assert self.dest2.join('hello').check() py.test.raises(IOError, "rsync.send()") + assert rsync.send(raises=False) is None def test_rsync_default_reporting(self): source = self.source From fijal at codespeak.net Thu Feb 8 17:02:22 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Thu, 8 Feb 2007 17:02:22 +0100 (CET) Subject: [py-svn] r38175 - py/trunk/py/test/rsession Message-ID: <20070208160222.8C5AE10098@code0.codespeak.net> Author: fijal Date: Thu Feb 8 17:02:15 2007 New Revision: 38175 Modified: py/trunk/py/test/rsession/hostmanage.py Log: Fix Modified: py/trunk/py/test/rsession/hostmanage.py ============================================================================== --- py/trunk/py/test/rsession/hostmanage.py (original) +++ py/trunk/py/test/rsession/hostmanage.py Thu Feb 8 17:02:15 2007 @@ -143,7 +143,7 @@ host, reporter, destrelpath, finishedcallback= lambda host=host, root=root: donecallback(host, root)) reporter(repevent.HostRSyncing(host, root, remotepath)) - rsync.send_if_targets() + rsync.send(raises=False) def setup_hosts(self, reporter): self.init_rsync(reporter) From hpk at codespeak.net Thu Feb 8 17:04:59 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 8 Feb 2007 17:04:59 +0100 (CET) Subject: [py-svn] r38176 - in py/trunk/py: execnet execnet/testing test/rsession Message-ID: <20070208160459.D05F610092@code0.codespeak.net> Author: hpk Date: Thu Feb 8 17:04:58 2007 New Revision: 38176 Modified: py/trunk/py/execnet/rsync.py py/trunk/py/execnet/testing/test_rsync.py py/trunk/py/test/rsession/hostmanage.py Log: make delete a per-host option (internally it is anyway) Modified: py/trunk/py/execnet/rsync.py ============================================================================== --- py/trunk/py/execnet/rsync.py (original) +++ py/trunk/py/execnet/rsync.py Thu Feb 8 17:04:58 2007 @@ -3,23 +3,16 @@ class RSync(object): - """ This class allows to synchronise files and directories - with one or multiple remote filesystems. - - An RSync instance allows to dynamically add remote targets - and then synchronizes the remote filesystems with - any provided source directory. + """ This class allows to send a directory structure (recursively) + to one or multiple remote filesystems. There is limited support for symlinks, which means that symlinks pointing to the sourcetree will be send "as is" while external symlinks will be just copied (regardless of existance of such a path on remote side). """ - def __init__(self, sourcedir, callback=None, verbose=True, **options): - for name in options: - assert name in ('delete') + def __init__(self, sourcedir, callback=None, verbose=True): self._sourcedir = str(sourcedir) - self._options = options self._verbose = verbose assert callback is None or callable(callback) self._callback = callback @@ -135,16 +128,19 @@ else: assert "Unknown command %s" % command - def add_target(self, gateway, destdir, finishedcallback=None): + def add_target(self, gateway, destdir, + finishedcallback=None, **options): """ Adds a remote target specified via a 'gateway' and a remote destination directory. """ assert finishedcallback is None or callable(finishedcallback) + for name in options: + assert name in ('delete',) def itemcallback(req): self._receivequeue.put((channel, req)) channel = gateway.remote_exec(REMOTE_SOURCE) channel.setcallback(itemcallback, endmarker = None) - channel.send((str(destdir), self._options)) + channel.send((str(destdir), options)) self._channels[channel] = finishedcallback def _broadcast(self, msg): Modified: py/trunk/py/execnet/testing/test_rsync.py ============================================================================== --- py/trunk/py/execnet/testing/test_rsync.py (original) +++ py/trunk/py/execnet/testing/test_rsync.py Thu Feb 8 17:04:58 2007 @@ -50,12 +50,12 @@ rsync.send() assert dest.join('subdir', 'file1').check(file=1) assert dest2.join('subdir', 'file1').check(file=1) - rsync = RSync(source, delete=True) + rsync = RSync(source) + rsync.add_target(gw, dest, delete=True) rsync.add_target(gw2, dest2) - rsync.add_target(gw, dest) rsync.send() assert not dest.join('subdir', 'file1').check() - assert not dest2.join('subdir', 'file1').check() + assert dest2.join('subdir', 'file1').check() def test_dirsync_twice(self): source = self.source Modified: py/trunk/py/test/rsession/hostmanage.py ============================================================================== --- py/trunk/py/test/rsession/hostmanage.py (original) +++ py/trunk/py/test/rsession/hostmanage.py Thu Feb 8 17:04:58 2007 @@ -77,7 +77,6 @@ if 'ignores' in kwargs: ignores = kwargs.pop('ignores') self._ignores = ignores or [] - kwargs['delete'] = True super(HostRSync, self).__init__(source, **kwargs) def filter(self, path): @@ -106,7 +105,9 @@ remotepath = os.path.join(remotepath, destrelpath) super(HostRSync, self).add_target(gw, remotepath, - finishedcallback) + finishedcallback, + delete=True, + ) return remotepath class HostManager(object): From guido at codespeak.net Thu Feb 8 17:28:34 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Thu, 8 Feb 2007 17:28:34 +0100 (CET) Subject: [py-svn] r38179 - py/trunk/py/bin Message-ID: <20070208162834.18C741009C@code0.codespeak.net> Author: guido Date: Thu Feb 8 17:28:33 2007 New Revision: 38179 Modified: py/trunk/py/bin/_update_website.py Log: Allowing to rsync also when tests are failing using a --ignorefail switch, and added a --help switch that shows a short help message. Modified: py/trunk/py/bin/_update_website.py ============================================================================== --- py/trunk/py/bin/_update_website.py (original) +++ py/trunk/py/bin/_update_website.py Thu Feb 8 17:28:33 2007 @@ -42,13 +42,16 @@ status = py.std.os.system(cmd) return status -def main(pkgpath, apidocspath, rhost, rpath, args=''): +def main(pkgpath, apidocspath, rhost, rpath, args='', ignorefail=False): print 'running tests' errors = run_tests(pkgpath, args) if errors: print >>sys.stderr, \ 'Errors while running the unit tests: %s' % (errors,) - sys.exit(1) + if not ignorefail: + print >>sys.stderr, ('if you want to continue the update ' + 'regardless of failures, use --ignorefail') + sys.exit(1) print 'rsyncing' gateway = py.execnet.SshGateway(rhost) @@ -58,9 +61,26 @@ sys.exit(1) if __name__ == '__main__': + args = sys.argv[1:] + if '--help' in args or '-h' in args: + print 'usage: %s [options]' + print + print 'run the py lib tests and update the py lib website' + print 'options:' + print ' --ignorefail: ignore errors in the unit tests and always' + print ' try to rsync' + print ' --help: show this message' + print + print 'any additional arguments are passed on as-is to the py.test' + print 'child process' + sys.exit() + ignorefail = False + if '--ignorefail' in args: + args.remove('--ignorefail') + ignorefail = True args = ' '.join(sys.argv[1:]) pkgpath = py.__package__.getpath() apidocspath = pkgpath.dirpath().join('apigen') main(pkgpath, apidocspath, 'codespeak.net', - '/home/guido/rsynctests', args) + '/home/guido/rsynctests', args, ignorefail) From hpk at codespeak.net Thu Feb 8 18:24:32 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 8 Feb 2007 18:24:32 +0100 (CET) Subject: [py-svn] r38181 - py/trunk/py/bin Message-ID: <20070208172432.1CAD910084@code0.codespeak.net> Author: hpk Date: Thu Feb 8 18:24:30 2007 New Revision: 38181 Modified: py/trunk/py/bin/_update_website.py Log: delete is now an option per-host Modified: py/trunk/py/bin/_update_website.py ============================================================================== --- py/trunk/py/bin/_update_website.py (original) +++ py/trunk/py/bin/_update_website.py Thu Feb 8 18:24:30 2007 @@ -18,8 +18,8 @@ pkgpath.copy(tempdir.ensure(pkgpath.basename, dir=True)) apidocspath.copy(tempdir.ensure(apidocspath.basename, dir=True)) - rs = py.execnet.RSync(tempdir, delete=True) - rs.add_target(gateway, remotepath) + rs = py.execnet.RSync(tempdir) + rs.add_target(gateway, remotepath, delete=True) rs.send() def run_tests(pkgpath, args=''): From guido at codespeak.net Thu Feb 8 18:25:35 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Thu, 8 Feb 2007 18:25:35 +0100 (CET) Subject: [py-svn] r38182 - in py/trunk/py/test: . testing Message-ID: <20070208172535.21E8310086@code0.codespeak.net> Author: guido Date: Thu Feb 8 18:25:32 2007 New Revision: 38182 Modified: py/trunk/py/test/config.py py/trunk/py/test/testing/test_config.py Log: Made that ensuretemp() creates a new dir after forks. Modified: py/trunk/py/test/config.py ============================================================================== --- py/trunk/py/test/config.py (original) +++ py/trunk/py/test/config.py Thu Feb 8 18:25:32 2007 @@ -8,12 +8,15 @@ # XXX move to Config class basetemp = None +_pid = None def ensuretemp(string, dir=1): """ return temporary directory path with the given string as the trailing part. """ - global basetemp - if basetemp is None: + global basetemp, _pid + currpid = py.std.os.getpid() + if basetemp is None or _pid != currpid: + _pid = currpid basetemp = py.path.local.make_numbered_dir(prefix='pytest-') return basetemp.ensure(string, dir=dir) Modified: py/trunk/py/test/testing/test_config.py ============================================================================== --- py/trunk/py/test/testing/test_config.py (original) +++ py/trunk/py/test/testing/test_config.py Thu Feb 8 18:25:32 2007 @@ -10,6 +10,21 @@ assert d1 == d2 assert d1.check(dir=1) +def test_ensuretemp_fork(): + os = py.std.os + org_getpid = os.getpid + currpid = 0 + def getpid(): + return currpid + try: + os.getpid = getpid + d1 = py.test.ensuretemp('hello') + currpid = 1 + d2 = py.test.ensuretemp('hello') + finally: + os.getpid = org_getpid + assert d1 != d2 + def test_config_cmdline_options(): o = py.test.ensuretemp('configoptions') o.ensure("conftest.py").write(py.code.Source(""" From guido at codespeak.net Thu Feb 8 18:28:57 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Thu, 8 Feb 2007 18:28:57 +0100 (CET) Subject: [py-svn] r38183 - in py/trunk/py: apigen apigen/testing misc/testing Message-ID: <20070208172857.B59F910087@code0.codespeak.net> Author: guido Date: Thu Feb 8 18:28:55 2007 New Revision: 38183 Modified: py/trunk/py/apigen/apigen.py py/trunk/py/apigen/htmlgen.py py/trunk/py/apigen/testing/test_apigen_functional.py py/trunk/py/misc/testing/test_update_website.py Log: Made that py.execnet.Channel (referring to py.__.execnet.channel.Channel) is picked up by the apigen doc generator, fixed some bugs that made that that didn't work previously. Modified: py/trunk/py/apigen/apigen.py ============================================================================== --- py/trunk/py/apigen/apigen.py (original) +++ py/trunk/py/apigen/apigen.py Thu Feb 8 18:28:55 2007 @@ -29,7 +29,7 @@ def get_documentable_items(pkgdir): pkgname, pkgdict = get_documentable_items_pkgdir(pkgdir) from py.__.execnet.channel import Channel - # pkgdict['execnet.Channel'] = Channel # XXX doesn't work + pkgdict['execnet.Channel'] = Channel return pkgname, pkgdict def build(pkgdir, dsa, capture): Modified: py/trunk/py/apigen/htmlgen.py ============================================================================== --- py/trunk/py/apigen/htmlgen.py (original) +++ py/trunk/py/apigen/htmlgen.py Thu Feb 8 18:28:55 2007 @@ -136,7 +136,7 @@ break return snippet -def get_obj(pkg, dotted_name): +def get_obj(dsa, pkg, dotted_name): full_dotted_name = '%s.%s' % (pkg.__name__, dotted_name) if dotted_name == '': return pkg @@ -146,8 +146,11 @@ marker = [] ret = getattr(ret, item, marker) if ret is marker: - raise NameError('can not access %s in %s' % (item, - full_dotted_name)) + try: + return dsa.get_obj(dotted_name) + except KeyError: + raise NameError('can not access %s in %s' % (item, + full_dotted_name)) return ret # the PageBuilder classes take care of producing the docs (using the stuff @@ -314,7 +317,7 @@ def build_callable_view(self, dotted_name): """ build the html for a class method """ # XXX we may want to have seperate - func = get_obj(self.pkg, dotted_name) + func = get_obj(self.dsa, self.pkg, dotted_name) docstring = func.__doc__ if docstring: docstring = deindent(docstring) @@ -348,7 +351,7 @@ def build_class_view(self, dotted_name): """ build the html for a class """ - cls = get_obj(self.pkg, dotted_name) + cls = get_obj(self.dsa, self.pkg, dotted_name) # XXX is this a safe check? try: sourcefile = inspect.getsourcefile(cls) @@ -419,7 +422,7 @@ def build_namespace_view(self, namespace_dotted_name, item_dotted_names): """ build the html for a namespace (module) """ - obj = get_obj(self.pkg, namespace_dotted_name) + obj = get_obj(self.dsa, self.pkg, namespace_dotted_name) docstring = obj.__doc__ snippet = H.NamespaceDescription( H.NamespaceDef(namespace_dotted_name), Modified: py/trunk/py/apigen/testing/test_apigen_functional.py ============================================================================== --- py/trunk/py/apigen/testing/test_apigen_functional.py (original) +++ py/trunk/py/apigen/testing/test_apigen_functional.py Thu Feb 8 18:28:55 2007 @@ -110,7 +110,7 @@ def test_get_documentable_items(): fs_root, package_name = setup_fs_project('test_get_documentable_items') - pkgname, documentable = apigen.get_documentable_items( + pkgname, documentable = apigen.get_documentable_items_pkgdir( fs_root.join(package_name)) assert pkgname == 'pak' assert sorted(documentable.keys()) == [ Modified: py/trunk/py/misc/testing/test_update_website.py ============================================================================== --- py/trunk/py/misc/testing/test_update_website.py (original) +++ py/trunk/py/misc/testing/test_update_website.py Thu Feb 8 18:28:55 2007 @@ -35,13 +35,17 @@ def test_foo(): assert foo(1) == 2 """)) - initfile = pkgpath.ensure('__init__.py').write(py.code.Source("""\ + initfile = pkgpath.ensure('__init__.py').write(py.code.Source(""" import py from py.__.initpkg import initpkg initpkg(__name__, exportdefs={ 'sub.foo': ('./mod.py', 'foo'), }) """)) + initfile = pkgpath.ensure('apigen/apigen.py').write(py.code.Source(""" + from py.__.apigen.apigen import build, \ + get_documentable_items_pkgdir as get_documentable_items + """)) return pkgpath def test_run_tests(): From hpk at codespeak.net Thu Feb 8 18:39:05 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 8 Feb 2007 18:39:05 +0100 (CET) Subject: [py-svn] r38184 - py/trunk/py/test Message-ID: <20070208173905.9684F1007E@code0.codespeak.net> Author: hpk Date: Thu Feb 8 18:39:03 2007 New Revision: 38184 Modified: py/trunk/py/test/config.py Log: avoid globals and add a comment Modified: py/trunk/py/test/config.py ============================================================================== --- py/trunk/py/test/config.py (original) +++ py/trunk/py/test/config.py Thu Feb 8 18:39:03 2007 @@ -6,18 +6,17 @@ optparse = py.compat.optparse -# XXX move to Config class -basetemp = None -_pid = None -def ensuretemp(string, dir=1): +def ensuretemp(string, dir=1, _pid2dir={}): """ return temporary directory path with the given string as the trailing part. """ - global basetemp, _pid - currpid = py.std.os.getpid() - if basetemp is None or _pid != currpid: - _pid = currpid + # we may be in a forking situation and want to use + # separate dirs then (XXX or not?) + pid = py.std.os.getpid() + basetemp = _pid2dir.get(pid, None) + if basetemp is None: basetemp = py.path.local.make_numbered_dir(prefix='pytest-') + _pid2dir[pid] = basetemp return basetemp.ensure(string, dir=dir) class CmdOptions(object): From hpk at codespeak.net Thu Feb 8 19:35:26 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 8 Feb 2007 19:35:26 +0100 (CET) Subject: [py-svn] r38192 - in py/trunk/py/test/rsession: . testing Message-ID: <20070208183526.96AD310088@code0.codespeak.net> Author: hpk Date: Thu Feb 8 19:35:18 2007 New Revision: 38192 Modified: py/trunk/py/test/rsession/hostmanage.py py/trunk/py/test/rsession/testing/test_hostmanage.py Log: streamlining localhost optimization handling, and simplifying the test a bit. Modified: py/trunk/py/test/rsession/hostmanage.py ============================================================================== --- py/trunk/py/test/rsession/hostmanage.py (original) +++ py/trunk/py/test/rsession/hostmanage.py Thu Feb 8 19:35:18 2007 @@ -17,14 +17,10 @@ def __init__(self, spec): parts = spec.split(':', 1) self.hostname = parts.pop(0) - if parts and parts[0]: - self.relpath = parts[0] - else: - self.relpath = "pytestcache-" + self.hostname - if spec.find(':') == -1 and self.hostname == 'localhost': - self.rsync_flag = False - else: - self.rsync_flag = True + self.relpath = parts and parts.pop(0) or "" + if not self.relpath and self.hostname != "localhost": + self.relpath = "pytestcache-%s" % self.hostname + assert not parts self.hostid = self._getuniqueid(self.hostname) def _getuniqueid(self, hostname): @@ -33,23 +29,24 @@ l.append(hostid) return hostid - def initgateway(self, python="python"): - assert not hasattr(self, 'gw') + def initgateway(self, python="python", topdir=None): if self.hostname == "localhost": - gw = py.execnet.PopenGateway(python=python) + self.gw = py.execnet.PopenGateway(python=python) else: - gw = py.execnet.SshGateway(self.hostname, - remotepython=python) - self.gw = gw - channel = gw.remote_exec(py.code.Source( + self.gw = py.execnet.SshGateway(self.hostname, + remotepython=python) + relpath = self.relpath or topdir + assert relpath + channel = self.gw.remote_exec(py.code.Source( gethomedir, getpath_relto_home, """ import os os.chdir(gethomedir()) - newdir = getpath_relto_home(%r) - # we intentionally don't ensure that 'newdir' exists - channel.send(newdir) - """ % str(self.relpath) + path = %r + if path: + path = getpath_relto_home(path) + channel.send(path) + """ % str(relpath) )) self.gw_remotepath = channel.receive() @@ -71,13 +68,13 @@ class HostRSync(py.execnet.RSync): """ RSyncer that filters out common files """ - def __init__(self, source, *args, **kwargs): + def __init__(self, sourcedir, *args, **kwargs): self._synced = {} ignores= None if 'ignores' in kwargs: ignores = kwargs.pop('ignores') self._ignores = ignores or [] - super(HostRSync, self).__init__(source, **kwargs) + super(HostRSync, self).__init__(sourcedir=sourcedir, **kwargs) def filter(self, path): path = py.path.local(path) @@ -91,20 +88,22 @@ return True def add_target_host(self, host, reporter=lambda x: None, - destrelpath=None, finishedcallback=None): - key = host.hostname, host.relpath - if not host.rsync_flag or key in self._synced: + destrelpath="", finishedcallback=None): + remotepath = host.relpath + key = host.hostname, remotepath + if host.hostname == "localhost" and not remotepath: + p = py.path.local(host.gw_remotepath) + assert p.join(destrelpath) == self._sourcedir + self._synced[key] = True + if key in self._synced: if finishedcallback: finishedcallback() return False self._synced[key] = True # the follow attributes are set from host.initgateway() - gw = host.gw - remotepath = host.gw_remotepath - if destrelpath is not None: + if destrelpath: remotepath = os.path.join(remotepath, destrelpath) - super(HostRSync, self).add_target(gw, - remotepath, + super(HostRSync, self).add_target(host.gw, remotepath, finishedcallback, delete=True, ) @@ -123,9 +122,9 @@ self.roots = roots def prepare_gateways(self, reporter): - dist_remotepython = self.config.getvalue("dist_remotepython") + python = self.config.getvalue("dist_remotepython") for host in self.hosts: - host.initgateway(python=dist_remotepython) + host.initgateway(python=python, topdir=self.config.topdir) reporter(repevent.HostGatewayReady(host, self.roots)) host.gw.host = host Modified: py/trunk/py/test/rsession/testing/test_hostmanage.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_hostmanage.py (original) +++ py/trunk/py/test/rsession/testing/test_hostmanage.py Thu Feb 8 19:35:18 2007 @@ -26,7 +26,7 @@ def test_defaultpath(self): x = HostInfo("localhost:") assert x.hostname == "localhost" - assert x.relpath == "pytestcache-" + x.hostname + assert not x.relpath def test_path(self): x = HostInfo("localhost:/tmp") @@ -193,18 +193,15 @@ assert not self.dest.join("dir6").check() def test_hostmanage_optimise_localhost(self): - def add_target(self, a, b, c): - assert 0, "Should not rsync here" - try: - config = py.test.config._reparse([self.source]) - old_add_target = HostRSync.add_target - HostRSync.add_target = add_target - hm = HostManager(config, hosts=[HostInfo('localhost') for i in - range(3)]) - events = [] - hm.init_rsync(reporter=events.append) - finally: - HostRSync.add_target = old_add_target + hosts = [HostInfo("localhost") for i in range(3)] + config = py.test.config._reparse([self.source]) + hm = HostManager(config, hosts=hosts) + events = [] + hm.init_rsync(events.append) + for host in hosts: + assert host.gw_remotepath == str(self.source) + assert not host.relpath + assert events def test_getpath_relto_home(): x = getpath_relto_home("hello") From hpk at codespeak.net Thu Feb 8 20:02:34 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 8 Feb 2007 20:02:34 +0100 (CET) Subject: [py-svn] r38196 - in py/trunk/py/test/rsession: . testing Message-ID: <20070208190234.329D71007E@code0.codespeak.net> Author: hpk Date: Thu Feb 8 20:02:28 2007 New Revision: 38196 Modified: py/trunk/py/test/rsession/hostmanage.py py/trunk/py/test/rsession/repevent.py py/trunk/py/test/rsession/reporter.py py/trunk/py/test/rsession/testing/test_hostmanage.py py/trunk/py/test/rsession/testing/test_rest.py Log: reducing the callback-indirections for rsyncing and improving the reporting for localhosts non-rsyncs Modified: py/trunk/py/test/rsession/hostmanage.py ============================================================================== --- py/trunk/py/test/rsession/hostmanage.py (original) +++ py/trunk/py/test/rsession/hostmanage.py Thu Feb 8 20:02:28 2007 @@ -87,24 +87,24 @@ else: return True - def add_target_host(self, host, reporter=lambda x: None, - destrelpath="", finishedcallback=None): - remotepath = host.relpath - key = host.hostname, remotepath - if host.hostname == "localhost" and not remotepath: - p = py.path.local(host.gw_remotepath) - assert p.join(destrelpath) == self._sourcedir + def add_target_host(self, host, destrelpath="", reporter=lambda x: None): + remotepath = host.gw_remotepath + key = host.hostname, host.relpath + if destrelpath: + remotepath = os.path.join(remotepath, destrelpath) + if host.hostname == "localhost" and remotepath == self._sourcedir: self._synced[key] = True + synced = key in self._synced + reporter(repevent.HostRSyncing(host, self._sourcedir, + remotepath, synced)) + def hostrsynced(host=host): + reporter(repevent.HostRSyncRootReady(host, self._sourcedir)) if key in self._synced: - if finishedcallback: - finishedcallback() - return False + hostrsynced() + return self._synced[key] = True - # the follow attributes are set from host.initgateway() - if destrelpath: - remotepath = os.path.join(remotepath, destrelpath) super(HostRSync, self).add_target(host.gw, remotepath, - finishedcallback, + finishedcallback=hostrsynced, delete=True, ) return remotepath @@ -129,20 +129,15 @@ host.gw.host = host def init_rsync(self, reporter): - # send each rsync root ignores = self.config.getvalue_pathlist("dist_rsync_ignore") self.prepare_gateways(reporter) + # send each rsync root for root in self.roots: rsync = HostRSync(root, ignores=ignores, verbose=self.config.option.verbose) destrelpath = root.relto(self.config.topdir) for host in self.hosts: - def donecallback(host, root): - reporter(repevent.HostRSyncRootReady(host, root)) - remotepath = rsync.add_target_host( - host, reporter, destrelpath, finishedcallback= - lambda host=host, root=root: donecallback(host, root)) - reporter(repevent.HostRSyncing(host, root, remotepath)) + rsync.add_target_host(host, destrelpath, reporter) rsync.send(raises=False) def setup_hosts(self, reporter): Modified: py/trunk/py/test/rsession/repevent.py ============================================================================== --- py/trunk/py/test/rsession/repevent.py (original) +++ py/trunk/py/test/rsession/repevent.py Thu Feb 8 20:02:28 2007 @@ -74,10 +74,11 @@ pass class HostRSyncing(ReportEvent): - def __init__(self, host, root, remotepath): + def __init__(self, host, root, remotepath, synced): self.host = host self.root = root self.remotepath = remotepath + self.synced = synced class HostGatewayReady(ReportEvent): def __init__(self, host, roots): Modified: py/trunk/py/test/rsession/reporter.py ============================================================================== --- py/trunk/py/test/rsession/reporter.py (original) +++ py/trunk/py/test/rsession/reporter.py Thu Feb 8 20:02:28 2007 @@ -58,11 +58,16 @@ def report_HostRSyncing(self, item): hostrepr = self._hostrepr(item.host) - if not item.remotepath: - print "%15s: skip duplicate rsync of %r" % ( - hostrepr, item.root.basename) + if item.synced: + if (item.host.hostname == "localhost" and + item.root == item.remotepath): + print "%15s: skipping inplace rsync of %r" %( + hostrepr, item.remotepath) + else: + print "%15s: skip duplicate rsync to %r" % ( + hostrepr, str(item.root)) else: - print "%15s: rsync %r to remote %s" % (hostrepr, + print "%15s: rsync %r to remote %r" % (hostrepr, item.root.basename, item.remotepath) Modified: py/trunk/py/test/rsession/testing/test_hostmanage.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_hostmanage.py (original) +++ py/trunk/py/test/rsession/testing/test_hostmanage.py Thu Feb 8 20:02:28 2007 @@ -132,6 +132,7 @@ rsync = HostRSync(self.source) l = [] h1.initgateway() + h2.initgateway() res1 = rsync.add_target_host(h1) assert res1 res2 = rsync.add_target_host(h2) Modified: py/trunk/py/test/rsession/testing/test_rest.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_rest.py (original) +++ py/trunk/py/test/rsession/testing/test_rest.py Thu Feb 8 20:02:28 2007 @@ -53,7 +53,7 @@ def test_report_HostRSyncing(self): event = repevent.HostRSyncing(HostInfo('localhost:/foo/bar'), "a", - "b") + "b", False) reporter.report(event) assert stdout.getvalue() == ('::\n\n localhost: RSYNC ==> ' '/foo/bar\n\n') From hpk at codespeak.net Thu Feb 8 20:11:16 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 8 Feb 2007 20:11:16 +0100 (CET) Subject: [py-svn] r38197 - py/trunk/py/test/rsession Message-ID: <20070208191116.E7C441007E@code0.codespeak.net> Author: hpk Date: Thu Feb 8 20:10:58 2007 New Revision: 38197 Modified: py/trunk/py/test/rsession/hostmanage.py py/trunk/py/test/rsession/reporter.py Log: slight reporting fix Modified: py/trunk/py/test/rsession/hostmanage.py ============================================================================== --- py/trunk/py/test/rsession/hostmanage.py (original) +++ py/trunk/py/test/rsession/hostmanage.py Thu Feb 8 20:10:58 2007 @@ -95,7 +95,7 @@ if host.hostname == "localhost" and remotepath == self._sourcedir: self._synced[key] = True synced = key in self._synced - reporter(repevent.HostRSyncing(host, self._sourcedir, + reporter(repevent.HostRSyncing(host, py.path.local(self._sourcedir), remotepath, synced)) def hostrsynced(host=host): reporter(repevent.HostRSyncRootReady(host, self._sourcedir)) Modified: py/trunk/py/test/rsession/reporter.py ============================================================================== --- py/trunk/py/test/rsession/reporter.py (original) +++ py/trunk/py/test/rsession/reporter.py Thu Feb 8 20:10:58 2007 @@ -65,7 +65,7 @@ hostrepr, item.remotepath) else: print "%15s: skip duplicate rsync to %r" % ( - hostrepr, str(item.root)) + hostrepr, item.root) else: print "%15s: rsync %r to remote %r" % (hostrepr, item.root.basename, From hpk at codespeak.net Thu Feb 8 20:15:19 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 8 Feb 2007 20:15:19 +0100 (CET) Subject: [py-svn] r38198 - py/trunk/py/test/rsession Message-ID: <20070208191519.3335C1007E@code0.codespeak.net> Author: hpk Date: Thu Feb 8 20:15:05 2007 New Revision: 38198 Modified: py/trunk/py/test/rsession/hostmanage.py Log: almost cosmetic Modified: py/trunk/py/test/rsession/hostmanage.py ============================================================================== --- py/trunk/py/test/rsession/hostmanage.py (original) +++ py/trunk/py/test/rsession/hostmanage.py Thu Feb 8 20:15:05 2007 @@ -52,9 +52,7 @@ def __str__(self): return "" % (self.hostname, self.relpath) - - def __repr__(self): - return str(self) + __repr__ = __str__ def __hash__(self): return hash(self.hostid) @@ -63,7 +61,7 @@ return self.hostid == other.hostid def __ne__(self, other): - return not self == other + return not self.hostid == other.hostid class HostRSync(py.execnet.RSync): """ RSyncer that filters out common files From hpk at codespeak.net Thu Feb 8 20:23:09 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 8 Feb 2007 20:23:09 +0100 (CET) Subject: [py-svn] r38201 - py/trunk/py/misc Message-ID: <20070208192309.B08E210084@code0.codespeak.net> Author: hpk Date: Thu Feb 8 20:23:06 2007 New Revision: 38201 Modified: py/trunk/py/misc/conftest-socketgatewayrun.py Log: fixing the windows conftest example to work with the new rsync interface Modified: py/trunk/py/misc/conftest-socketgatewayrun.py ============================================================================== --- py/trunk/py/misc/conftest-socketgatewayrun.py (original) +++ py/trunk/py/misc/conftest-socketgatewayrun.py Thu Feb 8 20:23:06 2007 @@ -34,7 +34,6 @@ def _initslavegateway(self): print "MASTER: initializing remote socket gateway" gw = py.execnet.SocketGateway(*self.socketserveradr) - rsync = MyRSync(delete=True) pkgname = 'py' # xxx flexibilize channel = gw.remote_exec(""" import os @@ -43,9 +42,10 @@ channel.send((topdir, pkgdir)) """ % (pkgname,)) remotetopdir, remotepkgdir = channel.receive() - rsync.add_target(gw, remotepkgdir) sendpath = py.path.local(py.__file__).dirpath() - rsync.send(sendpath) + rsync = MyRSync(sendpath) + rsync.add_target(gw, remotepkgdir, delete=True) + rsync.send() channel = gw.remote_exec(""" import os, sys path = %r # os.path.abspath From hpk at codespeak.net Thu Feb 8 20:48:34 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 8 Feb 2007 20:48:34 +0100 (CET) Subject: [py-svn] r38203 - in py/trunk/py: magic path path/local path/testing Message-ID: <20070208194834.A27FD1007E@code0.codespeak.net> Author: hpk Date: Thu Feb 8 20:48:31 2007 New Revision: 38203 Modified: py/trunk/py/magic/greenlet.py py/trunk/py/path/common.py py/trunk/py/path/local/local.py py/trunk/py/path/testing/fscommon.py Log: rename getpymodule/getpycodeobj to "_" methods (which can build C modules on the fly) it's not clear they are still useful this way and they are easy to confuse with pyimport() Modified: py/trunk/py/magic/greenlet.py ============================================================================== --- py/trunk/py/magic/greenlet.py (original) +++ py/trunk/py/magic/greenlet.py Thu Feb 8 20:48:31 2007 @@ -7,4 +7,4 @@ import py gdir = py.path.local(py.__file__).dirpath() path = gdir.join('c-extension', 'greenlet', 'greenlet.c') - greenlet = path.getpymodule().greenlet + greenlet = path._getpymodule().greenlet Modified: py/trunk/py/path/common.py ============================================================================== --- py/trunk/py/path/common.py (original) +++ py/trunk/py/path/common.py Thu Feb 8 20:48:31 2007 @@ -370,14 +370,14 @@ self.copy(target) self.remove() - def getpymodule(self): + def _getpymodule(self): """resolve this path to a module python object. """ modname = str(self) modname = modname.replace('.', self.sep) try: return sys.modules[modname] except KeyError: - co = self.getpycodeobj() + co = self._getpycodeobj() mod = py.std.new.module(modname) mod.__file__ = PathStr(self) if self.basename == '__init__.py': @@ -390,7 +390,7 @@ raise return mod - def getpycodeobj(self): + def _getpycodeobj(self): """ read the path and compile it to a py.code.Code object. """ s = self.read('rU') # XXX str(self) should show up somewhere in the code's filename @@ -421,7 +421,7 @@ p = p.new(basename=name).join('__init__.py') if not p.check(): return None # not found - submodule = p.getpymodule() + submodule = p._getpymodule() if parent is not None: setattr(parent, name, submodule) modules.append(submodule) Modified: py/trunk/py/path/local/local.py ============================================================================== --- py/trunk/py/path/local/local.py (original) +++ py/trunk/py/path/local/local.py Thu Feb 8 20:48:31 2007 @@ -426,15 +426,15 @@ raise return mod - def getpymodule(self): + def _getpymodule(self): """resolve this path to a module python object. """ if self.ext != '.c': - return super(LocalPath, self).getpymodule() + return super(LocalPath, self)._getpymodule() from py.__.misc.buildcmodule import make_module_from_c mod = make_module_from_c(self) return mod - def getpycodeobj(self): + def _getpycodeobj(self): """ read the path and compile it to a code object. """ dotpy = self.check(ext='.py') if dotpy: Modified: py/trunk/py/path/testing/fscommon.py ============================================================================== --- py/trunk/py/path/testing/fscommon.py (original) +++ py/trunk/py/path/testing/fscommon.py Thu Feb 8 20:48:31 2007 @@ -216,8 +216,8 @@ assert dest.join('otherfile').check(file=1) assert not source.join('sampledir').check() - def test_getpymodule(self): - obj = self.root.join('execfile').getpymodule() + def test__getpymodule(self): + obj = self.root.join('execfile')._getpymodule() assert obj.x == 42 def test_not_has_resolve(self): @@ -225,22 +225,22 @@ # py.path.extpy assert not hasattr(self.root, 'resolve') - def test_getpymodule_a(self): + def test__getpymodule_a(self): otherdir = self.root.join('otherdir') - mod = otherdir.join('a.py').getpymodule() + mod = otherdir.join('a.py')._getpymodule() assert mod.result == "got it" - def test_getpymodule_b(self): + def test__getpymodule_b(self): otherdir = self.root.join('otherdir') - mod = otherdir.join('b.py').getpymodule() + mod = otherdir.join('b.py')._getpymodule() assert mod.stuff == "got it" - def test_getpymodule_c(self): + def test__getpymodule_c(self): otherdir = self.root.join('otherdir') - mod = otherdir.join('c.py').getpymodule() + mod = otherdir.join('c.py')._getpymodule() assert mod.value == "got it" - def test_getpymodule_d(self): + def test__getpymodule_d(self): otherdir = self.root.join('otherdir') - mod = otherdir.join('d.py').getpymodule() + mod = otherdir.join('d.py')._getpymodule() assert mod.value2 == "got it" From hpk at codespeak.net Thu Feb 8 21:32:01 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 8 Feb 2007 21:32:01 +0100 (CET) Subject: [py-svn] r38207 - in py/trunk/py/test: . testing Message-ID: <20070208203201.0D6481007E@code0.codespeak.net> Author: hpk Date: Thu Feb 8 21:31:50 2007 New Revision: 38207 Modified: py/trunk/py/test/config.py py/trunk/py/test/testing/test_config.py Log: revert 38182 and 38184 changes to ensuretemp() because getpid() is different per thread (and if e.g. run via execnet in threads ... it creates new tempdirectories all the time) let's consider this sometime else Modified: py/trunk/py/test/config.py ============================================================================== --- py/trunk/py/test/config.py (original) +++ py/trunk/py/test/config.py Thu Feb 8 21:31:50 2007 @@ -6,17 +6,15 @@ optparse = py.compat.optparse -def ensuretemp(string, dir=1, _pid2dir={}): +# XXX move to Config class +basetemp = None +def ensuretemp(string, dir=1): """ return temporary directory path with the given string as the trailing part. """ - # we may be in a forking situation and want to use - # separate dirs then (XXX or not?) - pid = py.std.os.getpid() - basetemp = _pid2dir.get(pid, None) + global basetemp if basetemp is None: basetemp = py.path.local.make_numbered_dir(prefix='pytest-') - _pid2dir[pid] = basetemp return basetemp.ensure(string, dir=dir) class CmdOptions(object): Modified: py/trunk/py/test/testing/test_config.py ============================================================================== --- py/trunk/py/test/testing/test_config.py (original) +++ py/trunk/py/test/testing/test_config.py Thu Feb 8 21:31:50 2007 @@ -10,21 +10,6 @@ assert d1 == d2 assert d1.check(dir=1) -def test_ensuretemp_fork(): - os = py.std.os - org_getpid = os.getpid - currpid = 0 - def getpid(): - return currpid - try: - os.getpid = getpid - d1 = py.test.ensuretemp('hello') - currpid = 1 - d2 = py.test.ensuretemp('hello') - finally: - os.getpid = org_getpid - assert d1 != d2 - def test_config_cmdline_options(): o = py.test.ensuretemp('configoptions') o.ensure("conftest.py").write(py.code.Source(""" From hpk at codespeak.net Thu Feb 8 21:56:14 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 8 Feb 2007 21:56:14 +0100 (CET) Subject: [py-svn] r38208 - py/trunk/py/path/local/testing Message-ID: <20070208205614.048A61007C@code0.codespeak.net> Author: hpk Date: Thu Feb 8 21:56:12 2007 New Revision: 38208 Modified: py/trunk/py/path/local/testing/test_local.py Log: make test safer against race conditiation (dist testing) Modified: py/trunk/py/path/local/testing/test_local.py ============================================================================== --- py/trunk/py/path/local/testing/test_local.py (original) +++ py/trunk/py/path/local/testing/test_local.py Thu Feb 8 21:56:12 2007 @@ -5,7 +5,7 @@ class LocalSetup: def setup_class(cls): - cls.root = py.test.ensuretemp('TestLocalPath') + cls.root = py.test.ensuretemp(cls.__name__) cls.root.ensure(dir=1) setuptestfs(cls.root) @@ -265,10 +265,10 @@ py.test.skip("Fails when run as boxed tests") root = self.tmpdir for i in range(10): - numdir = local.make_numbered_dir(prefix='base.', rootdir=root, + numdir = local.make_numbered_dir(prefix='base2.', rootdir=root, keep=2) assert numdir.check() - assert numdir.basename == 'base.%d' %i + assert numdir.basename == 'base2.%d' %i for j in range(i): assert numdir.new(ext=str(j)).check() From hpk at codespeak.net Thu Feb 8 23:14:03 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 8 Feb 2007 23:14:03 +0100 (CET) Subject: [py-svn] r38212 - py/trunk/py/test/rsession Message-ID: <20070208221403.945EE1007E@code0.codespeak.net> Author: hpk Date: Thu Feb 8 23:13:59 2007 New Revision: 38212 Modified: py/trunk/py/test/rsession/box.py Log: have each boxed test run use its own tempdir Modified: py/trunk/py/test/rsession/box.py ============================================================================== --- py/trunk/py/test/rsession/box.py (original) +++ py/trunk/py/test/rsession/box.py Thu Feb 8 23:13:59 2007 @@ -7,6 +7,7 @@ import os import sys import marshal +from py.__.test import config as pytestconfig PYTESTSTDOUT = "pyteststdout" PYTESTSTDERR = "pyteststderr" @@ -30,6 +31,7 @@ self.kwargs = kwargs def run(self, continuation=False): + # XXX we should not use py.test.ensuretemp here tempdir = py.test.ensuretemp("box%d" % self.count) self.count += 1 self.tempdir = tempdir @@ -78,6 +80,11 @@ try: if nice_level: os.nice(nice_level) + # with fork() we have duplicated py.test's basetemp + # directory so we unset it manually here. + # this may be expensive for some test setups, + # but that is what you get with boxing. + pytestconfig.basetemp = None retval = self.fun(*self.args, **self.kwargs) retvalf.write(marshal.dumps(retval)) finally: From guido at codespeak.net Thu Feb 8 23:19:13 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Thu, 8 Feb 2007 23:19:13 +0100 (CET) Subject: [py-svn] r38213 - py/trunk/py/apigen Message-ID: <20070208221913.0FBBF1007E@code0.codespeak.net> Author: guido Date: Thu Feb 8 23:19:10 2007 New Revision: 38213 Modified: py/trunk/py/apigen/html.py py/trunk/py/apigen/htmlgen.py py/trunk/py/apigen/style.css Log: Some small cleanups, made fonts a bit smaller and more consistent. Modified: py/trunk/py/apigen/html.py ============================================================================== --- py/trunk/py/apigen/html.py (original) +++ py/trunk/py/apigen/html.py Thu Feb 8 23:19:10 2007 @@ -112,12 +112,13 @@ super(H.SourceSnippet, self).__init__( link, H.div(*sourceels)) - class SourceDef(html.div): + class PythonSource(Content): + style = html.Style(font_size='0.8em') def __init__(self, *sourceels): - super(H.SourceDef, self).__init__( + super(H.PythonSource, self).__init__( H.div(*sourceels)) - class SourceCode(html.div): + class SourceBlock(html.div): style = html.Style(margin_top='1em', margin_bottom='1em') def __init__(self): self.linenotable = lntable = H.table(style='float: left') @@ -128,7 +129,7 @@ self.linetbody = ltbody = H.tbody() ltable.append(ltbody) - super(H.SourceCode, self).__init__(lntable, ltable) + super(H.SourceBlock, self).__init__(lntable, ltable) def add_line(self, lineno, els): if els == []: @@ -136,10 +137,11 @@ self.linenotbody.append(H.tr(H.td(lineno, class_='lineno'))) self.linetbody.append(H.tr(H.td(class_='code', *els))) - class NonPythonSource(html.pre): - pass # style = html.Style(margin_left='15em') + class NonPythonSource(Content): + def __init__(self, *args): + super(H.NonPythonSource, self).__init__(H.pre(*args)) - class DirList(html.div): + class DirList(Content): def __init__(self, dirs, files): dirs = [H.DirListItem(text, href) for (text, href) in dirs] files = [H.DirListItem(text, href) for (text, href) in files] Modified: py/trunk/py/apigen/htmlgen.py ============================================================================== --- py/trunk/py/apigen/htmlgen.py (original) +++ py/trunk/py/apigen/htmlgen.py Thu Feb 8 23:19:10 2007 @@ -124,7 +124,7 @@ return page def enumerate_and_color(codelines, firstlineno, enc): - snippet = H.SourceCode() + snippet = H.SourceBlock() tokenizer = source_color.Tokenizer(source_color.PythonSchema) for i, line in enumerate(codelines): try: @@ -230,7 +230,7 @@ source = fspath.read() sep = get_linesep(source) colored = enumerate_and_color(source.split(sep), 0, enc) - tag = H.SourceDef(colored) + tag = H.PythonSource(colored) nav = self.build_navigation(fspath) return tag, nav @@ -597,7 +597,7 @@ return cslinks def build_callsite(self, dotted_name, call_site, index): - tbtag = self.gen_traceback(dotted_name, reversed(call_site)) + tbtag = H.Content(self.gen_traceback(dotted_name, reversed(call_site))) parent_dotted_name, _ = split_of_last_part(dotted_name) nav = self.build_navigation(parent_dotted_name, False) id = 'callsite_%s_%s' % (dotted_name, index) Modified: py/trunk/py/apigen/style.css ============================================================================== --- py/trunk/py/apigen/style.css (original) +++ py/trunk/py/apigen/style.css Thu Feb 8 23:19:10 2007 @@ -80,10 +80,6 @@ background-color: white; } -.property { - font-size: 1.2em; -} - .callstackitem { border: 1px solid black; margin-bottom: 1em; From hpk at codespeak.net Thu Feb 8 23:24:46 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 8 Feb 2007 23:24:46 +0100 (CET) Subject: [py-svn] r38214 - py/trunk/py/test/rsession Message-ID: <20070208222446.6ABFD1007E@code0.codespeak.net> Author: hpk Date: Thu Feb 8 23:24:44 2007 New Revision: 38214 Modified: py/trunk/py/test/rsession/box.py Log: be a bit nicer: don't pollute /tmp with a new tempdir for each boxed test run. Also fix the counting of boxes (how did it ever work like this, anyway) Modified: py/trunk/py/test/rsession/box.py ============================================================================== --- py/trunk/py/test/rsession/box.py (original) +++ py/trunk/py/test/rsession/box.py Thu Feb 8 23:24:44 2007 @@ -14,11 +14,12 @@ PYTESTRETVAL = "pytestretval" import tempfile +import itertools from StringIO import StringIO +counter = itertools.count().next + class FileBox(object): - count = 0 - def __init__(self, fun, args=None, kwargs=None, config=None): if args is None: args = [] @@ -32,8 +33,8 @@ def run(self, continuation=False): # XXX we should not use py.test.ensuretemp here - tempdir = py.test.ensuretemp("box%d" % self.count) - self.count += 1 + count = counter() + tempdir = py.test.ensuretemp("box%d" % count) self.tempdir = tempdir self.PYTESTRETVAL = tempdir.join('retval') self.PYTESTSTDOUT = tempdir.join('stdout') @@ -81,10 +82,10 @@ if nice_level: os.nice(nice_level) # with fork() we have duplicated py.test's basetemp - # directory so we unset it manually here. + # directory so we set it manually here. # this may be expensive for some test setups, # but that is what you get with boxing. - pytestconfig.basetemp = None + pytestconfig.basetemp = self.tempdir.join("childbasetemp") retval = self.fun(*self.args, **self.kwargs) retvalf.write(marshal.dumps(retval)) finally: From guido at codespeak.net Thu Feb 8 23:47:32 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Thu, 8 Feb 2007 23:47:32 +0100 (CET) Subject: [py-svn] r38215 - py/trunk/py/apigen Message-ID: <20070208224732.9894A1007E@code0.codespeak.net> Author: guido Date: Thu Feb 8 23:47:28 2007 New Revision: 38215 Modified: py/trunk/py/apigen/html.py Log: Gotta love the XHTML validity checks... ;) Modified: py/trunk/py/apigen/html.py ============================================================================== --- py/trunk/py/apigen/html.py (original) +++ py/trunk/py/apigen/html.py Thu Feb 8 23:47:28 2007 @@ -99,7 +99,7 @@ if selected: self.attr.class_ = 'selected' - class BaseDescription(html.a): + class BaseDescription(html.span): pass class SourceSnippet(html.div): From guido at codespeak.net Thu Feb 8 23:50:02 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Thu, 8 Feb 2007 23:50:02 +0100 (CET) Subject: [py-svn] r38216 - py/trunk/py/misc Message-ID: <20070208225002.513B710086@code0.codespeak.net> Author: guido Date: Thu Feb 8 23:50:00 2007 New Revision: 38216 Modified: py/trunk/py/misc/conftest-socketgatewayrun.py Log: Adjusting the conftest-for-socketgateway example so it takes care of the -S option (like the default one does), this caused some test failure. Modified: py/trunk/py/misc/conftest-socketgatewayrun.py ============================================================================== --- py/trunk/py/misc/conftest-socketgatewayrun.py (original) +++ py/trunk/py/misc/conftest-socketgatewayrun.py Thu Feb 8 23:50:00 2007 @@ -16,6 +16,15 @@ import os +Option = py.test.config.Option + +option = py.test.config.addoptions("execnet options", + Option('-S', '', + action="store", dest="sshtarget", default=None, + help=("target to run tests requiring ssh, e.g. " + "user at codespeak.net")), + ) + class MyRSync(py.execnet.RSync): def filter(self, path): if path.endswith('.pyc') or path.endswith('~'): From guido at codespeak.net Fri Feb 9 00:21:41 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Fri, 9 Feb 2007 00:21:41 +0100 (CET) Subject: [py-svn] r38217 - in py/trunk/py: . execnet execnet/testing misc Message-ID: <20070208232141.C670A10063@code0.codespeak.net> Author: guido Date: Fri Feb 9 00:21:38 2007 New Revision: 38217 Added: py/trunk/py/execnet/conftest.py Modified: py/trunk/py/conftest.py py/trunk/py/execnet/testing/test_gateway.py py/trunk/py/misc/conftest-socketgatewayrun.py Log: Moved the -S option to a new conftest.py in the execnet dir to avoid nasty option re-definition problems. Modified: py/trunk/py/conftest.py ============================================================================== --- py/trunk/py/conftest.py (original) +++ py/trunk/py/conftest.py Fri Feb 9 00:21:38 2007 @@ -15,15 +15,4 @@ fulltrace = False showlocals = False nomagic = False - -import py -Option = py.test.config.Option - -option = py.test.config.addoptions("execnet options", - Option('-S', '', - action="store", dest="sshtarget", default=None, - help=("target to run tests requiring ssh, e.g. " - "user at codespeak.net")), - ) - dist_rsync_roots = ['.'] Added: py/trunk/py/execnet/conftest.py ============================================================================== --- (empty file) +++ py/trunk/py/execnet/conftest.py Fri Feb 9 00:21:38 2007 @@ -0,0 +1,10 @@ +import py +Option = py.test.config.Option + +option = py.test.config.addoptions("execnet options", + Option('-S', '', + action="store", dest="sshtarget", default=None, + help=("target to run tests requiring ssh, e.g. " + "user at codespeak.net")), + ) + Modified: py/trunk/py/execnet/testing/test_gateway.py ============================================================================== --- py/trunk/py/execnet/testing/test_gateway.py (original) +++ py/trunk/py/execnet/testing/test_gateway.py Fri Feb 9 00:21:38 2007 @@ -2,7 +2,7 @@ import os, sys, time, signal import py from py.__.execnet import gateway -from py.__.conftest import option +from py.__.execnet.conftest import option mypath = py.magic.autopath() from StringIO import StringIO Modified: py/trunk/py/misc/conftest-socketgatewayrun.py ============================================================================== --- py/trunk/py/misc/conftest-socketgatewayrun.py (original) +++ py/trunk/py/misc/conftest-socketgatewayrun.py Fri Feb 9 00:21:38 2007 @@ -16,15 +16,6 @@ import os -Option = py.test.config.Option - -option = py.test.config.addoptions("execnet options", - Option('-S', '', - action="store", dest="sshtarget", default=None, - help=("target to run tests requiring ssh, e.g. " - "user at codespeak.net")), - ) - class MyRSync(py.execnet.RSync): def filter(self, path): if path.endswith('.pyc') or path.endswith('~'): From hpk at codespeak.net Fri Feb 9 00:25:33 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 9 Feb 2007 00:25:33 +0100 (CET) Subject: [py-svn] r38218 - py/trunk/py/test/rsession Message-ID: <20070208232533.D1D891007C@code0.codespeak.net> Author: hpk Date: Fri Feb 9 00:25:32 2007 New Revision: 38218 Modified: py/trunk/py/test/rsession/box.py Log: i give up for now, somehow the local change in the FileBox affects --dist testing in ways i can't fully understand at the moment (i would think it only affects --boxed test runs). So we are back to share tempdirs when running boxed (causing failures elswhere). We care later, i guess. Modified: py/trunk/py/test/rsession/box.py ============================================================================== --- py/trunk/py/test/rsession/box.py (original) +++ py/trunk/py/test/rsession/box.py Fri Feb 9 00:25:32 2007 @@ -82,10 +82,13 @@ if nice_level: os.nice(nice_level) # with fork() we have duplicated py.test's basetemp - # directory so we set it manually here. + # directory so we want to set it manually here. # this may be expensive for some test setups, # but that is what you get with boxing. - pytestconfig.basetemp = self.tempdir.join("childbasetemp") + # XXX but we are called in more than strict boxing + # mode ("AsyncExecutor") so we can't do the following without + # inflicting on --dist speed, hum: + # pytestconfig.basetemp = self.tempdir.join("childbasetemp") retval = self.fun(*self.args, **self.kwargs) retvalf.write(marshal.dumps(retval)) finally: From hpk at codespeak.net Fri Feb 9 00:38:11 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 9 Feb 2007 00:38:11 +0100 (CET) Subject: [py-svn] r38219 - py/trunk/py/test Message-ID: <20070208233811.9F4CA10086@code0.codespeak.net> Author: hpk Date: Fri Feb 9 00:38:07 2007 New Revision: 38219 Modified: py/trunk/py/test/defaultconftest.py Log: looponfailing and exec are not so experimental Modified: py/trunk/py/test/defaultconftest.py ============================================================================== --- py/trunk/py/test/defaultconftest.py (original) +++ py/trunk/py/test/defaultconftest.py Fri Feb 9 00:38:07 2007 @@ -67,15 +67,15 @@ Option('', '--traceconfig', action="store_true", dest="traceconfig", default=False, help="trace considerations of conftest.py files."), - ) - - config._addoptions('EXPERIMENTAL options', Option('-f', '--looponfailing', action="store_true", dest="looponfailing", default=False, help="loop on failing test set."), Option('', '--exec', action="store", dest="executable", default=None, help="python executable to run the tests with."), + ) + + config._addoptions('EXPERIMENTAL options', Option('-d', '--dist', action="store_true", dest="dist", default=False, help="ad-hoc distribute tests across machines (requires conftest settings)"), From hpk at codespeak.net Fri Feb 9 00:42:02 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 9 Feb 2007 00:42:02 +0100 (CET) Subject: [py-svn] r38220 - py/trunk/py/doc Message-ID: <20070208234202.A8FFD1007C@code0.codespeak.net> Author: hpk Date: Fri Feb 9 00:41:59 2007 New Revision: 38220 Modified: py/trunk/py/doc/test.txt Log: moved -f and --exec in the documentation as well Modified: py/trunk/py/doc/test.txt ============================================================================== --- py/trunk/py/doc/test.txt (original) +++ py/trunk/py/doc/test.txt Fri Feb 9 00:41:59 2007 @@ -425,13 +425,6 @@ trace considerations of conftest.py files. Useful when you have various conftest.py files around and are unsure about their interaction. - -experimental options --------------------- - -**Note**: these options could change in the future. - - ``-f, --looponfailing`` Loop on failing test set. This is a feature you can use when you are trying to fix a number of failing tests: First all the tests are being run. If a @@ -445,6 +438,13 @@ versions of Python. + +experimental options +-------------------- + +**Note**: these options could change in the future. + + ``-d, --dist`` ad-hoc `distribute tests across machines`_ (requires conftest settings) From guido at codespeak.net Fri Feb 9 00:43:37 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Fri, 9 Feb 2007 00:43:37 +0100 (CET) Subject: [py-svn] r38221 - in py/trunk/py: . execnet execnet/testing Message-ID: <20070208234337.6CE2E1007C@code0.codespeak.net> Author: guido Date: Fri Feb 9 00:43:27 2007 New Revision: 38221 Removed: py/trunk/py/execnet/conftest.py Modified: py/trunk/py/conftest.py py/trunk/py/execnet/testing/test_gateway.py Log: Reverting previous changes after a discussion with hpk. Modified: py/trunk/py/conftest.py ============================================================================== --- py/trunk/py/conftest.py (original) +++ py/trunk/py/conftest.py Fri Feb 9 00:43:27 2007 @@ -15,4 +15,15 @@ fulltrace = False showlocals = False nomagic = False + +import py +Option = py.test.config.Option + +option = py.test.config.addoptions("execnet options", + Option('-S', '', + action="store", dest="sshtarget", default=None, + help=("target to run tests requiring ssh, e.g. " + "user at codespeak.net")), + ) + dist_rsync_roots = ['.'] Deleted: /py/trunk/py/execnet/conftest.py ============================================================================== --- /py/trunk/py/execnet/conftest.py Fri Feb 9 00:43:27 2007 +++ (empty file) @@ -1,10 +0,0 @@ -import py -Option = py.test.config.Option - -option = py.test.config.addoptions("execnet options", - Option('-S', '', - action="store", dest="sshtarget", default=None, - help=("target to run tests requiring ssh, e.g. " - "user at codespeak.net")), - ) - Modified: py/trunk/py/execnet/testing/test_gateway.py ============================================================================== --- py/trunk/py/execnet/testing/test_gateway.py (original) +++ py/trunk/py/execnet/testing/test_gateway.py Fri Feb 9 00:43:27 2007 @@ -2,7 +2,7 @@ import os, sys, time, signal import py from py.__.execnet import gateway -from py.__.execnet.conftest import option +from py.__.conftest import option mypath = py.magic.autopath() from StringIO import StringIO From hpk at codespeak.net Fri Feb 9 01:10:12 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 9 Feb 2007 01:10:12 +0100 (CET) Subject: [py-svn] r38224 - py/dist Message-ID: <20070209001012.E0A6D1007E@code0.codespeak.net> Author: hpk Date: Fri Feb 9 01:10:05 2007 New Revision: 38224 Added: py/dist/ - copied from r38223, py/trunk/ Log: snapshotting trunk to dist From hpk at codespeak.net Fri Feb 9 01:10:07 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 9 Feb 2007 01:10:07 +0100 (CET) Subject: [py-svn] r38223 - py/dist Message-ID: <20070209001007.0A4141007C@code0.codespeak.net> Author: hpk Date: Fri Feb 9 01:10:03 2007 New Revision: 38223 Removed: py/dist/ Log: removing py/dist in order to copy py/trunk From guido at codespeak.net Fri Feb 9 14:01:12 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Fri, 9 Feb 2007 14:01:12 +0100 (CET) Subject: [py-svn] r38246 - in py/trunk/py/apigen: . source Message-ID: <20070209130112.4934110098@code0.codespeak.net> Author: guido Date: Fri Feb 9 14:01:08 2007 New Revision: 38246 Modified: py/trunk/py/apigen/html.py py/trunk/py/apigen/source/color.py py/trunk/py/apigen/style.css Log: Fixed problems with line height in sources, added missing commas to seperate base classes, added all Python (2.5) keywords. Modified: py/trunk/py/apigen/html.py ============================================================================== --- py/trunk/py/apigen/html.py (original) +++ py/trunk/py/apigen/html.py Fri Feb 9 14:01:08 2007 @@ -1,5 +1,6 @@ -from py.xml import html +import py +html = py.xml.html # HTML related stuff class H(html): @@ -26,7 +27,9 @@ def __init__(self, classname, bases, docstring, sourcelink, properties, methods): header = H.h1('class %s(' % (classname,)) - for name, href in bases: + for i, (name, href) in py.builtin.enumerate(bases): + if i > 0: + header.append(', ') link = name if href is not None: link = H.a(name, href=href) @@ -132,8 +135,6 @@ super(H.SourceBlock, self).__init__(lntable, ltable) def add_line(self, lineno, els): - if els == []: - els = [u'\xa0'] self.linenotbody.append(H.tr(H.td(lineno, class_='lineno'))) self.linetbody.append(H.tr(H.td(class_='code', *els))) Modified: py/trunk/py/apigen/source/color.py ============================================================================== --- py/trunk/py/apigen/source/color.py (original) +++ py/trunk/py/apigen/source/color.py Fri Feb 9 14:01:08 2007 @@ -7,11 +7,11 @@ comment = [('#', '\n'), ('#', '$')] multiline_string = ['"""', "'''"] string = ['"""', "'''", '"', "'"] - # XXX not complete - keyword = ['for', 'if', 'not', 'then', 'else', 'while', 'from', 'import', - 'try', 'except', 'finally', 'raise', 'print', 'exec', 'eval', - 'break', 'in', 'assert', 'None'] - alt_keyword = ['def', 'class', 'return', 'pass', 'yield'] + keyword = ['and', 'break', 'continue', 'elif', 'else', 'except', + 'finally', 'for', 'if', 'in', 'is', 'not', 'or', 'raise', + 'return', 'try', 'while', 'with', 'yield'] + alt_keyword = ['as', 'assert', 'class', 'def', 'del', 'exec', 'from', + 'global', 'import', 'lambda', 'pass', 'print'] class Token(object): data = None Modified: py/trunk/py/apigen/style.css ============================================================================== --- py/trunk/py/apigen/style.css (original) +++ py/trunk/py/apigen/style.css Fri Feb 9 14:01:08 2007 @@ -41,6 +41,7 @@ } .lineno { + line-height: 1.4em; text-align: right; color: #555; width: 3em; @@ -50,6 +51,7 @@ } .code { + line-height: 1.4em; padding-left: 1em; white-space: pre; font-family: monospace, Monaco; From fijal at codespeak.net Fri Feb 9 14:24:10 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Fri, 9 Feb 2007 14:24:10 +0100 (CET) Subject: [py-svn] r38249 - py/trunk/py/test Message-ID: <20070209132410.5B3AA1009B@code0.codespeak.net> Author: fijal Date: Fri Feb 9 14:24:08 2007 New Revision: 38249 Modified: py/trunk/py/test/config.py Log: It seems that there was somehow different idea in mind, but I actually do not get it. Right now there is no way to override default session other than from py.__.test import config; config.TerminalSession = 'name_to_import' which is scary. Holger please take a look. Modified: py/trunk/py/test/config.py ============================================================================== --- py/trunk/py/test/config.py (original) +++ py/trunk/py/test/config.py Fri Feb 9 14:24:08 2007 @@ -146,6 +146,10 @@ return self.conftest.rget(self.option.session) else: name = self._getsessionname() + try: + return self.conftest.rget(name) + except KeyError: + pass importpath = globals()[name] mod = __import__(importpath, None, None, '__doc__') return getattr(mod, name) From guido at codespeak.net Fri Feb 9 16:13:34 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Fri, 9 Feb 2007 16:13:34 +0100 (CET) Subject: [py-svn] r38262 - in py/trunk/py/apigen/source: . testing Message-ID: <20070209151334.16B50100A0@code0.codespeak.net> Author: guido Date: Fri Feb 9 16:13:30 2007 New Revision: 38262 Modified: py/trunk/py/apigen/source/color.py py/trunk/py/apigen/source/testing/test_color.py Log: Fixed support for tokenizing multi-line strings that use \ to span lines. Modified: py/trunk/py/apigen/source/color.py ============================================================================== --- py/trunk/py/apigen/source/color.py (original) +++ py/trunk/py/apigen/source/color.py Fri Feb 9 16:13:30 2007 @@ -12,6 +12,7 @@ 'return', 'try', 'while', 'with', 'yield'] alt_keyword = ['as', 'assert', 'class', 'def', 'del', 'exec', 'from', 'global', 'import', 'lambda', 'pass', 'print'] + linejoin = r'\\' class Token(object): data = None @@ -59,6 +60,12 @@ self._re_strings_full.append( re.compile(r'%s[^\\%s]+(\\.[^\\%s]*)*%s' % (d, d, d, d))) self._re_strings_empty.append(re.compile('%s%s' % (d, d))) + if schema.linejoin: + j = schema.linejoin + for d in schema.string + schema.multiline_string: + self._re_strings_multiline.append( + (re.compile('%s.*%s' % (d, j)), + re.compile('.*?%s' % (d,)))) for d in schema.multiline_string: self._re_strings_multiline.append((re.compile('%s.*' % (d,), re.S), re.compile('.*?%s' % (d,)))) @@ -105,6 +112,7 @@ def _check_multiline_strings(self, data): token = None for start, end in self._re_strings_multiline: + print dir(start), end m = start.match(data) if m: s = m.group(0) Modified: py/trunk/py/apigen/source/testing/test_color.py ============================================================================== --- py/trunk/py/apigen/source/testing/test_color.py (original) +++ py/trunk/py/apigen/source/testing/test_color.py Fri Feb 9 16:13:30 2007 @@ -75,6 +75,15 @@ res = list(t.tokenize('bar')) assert res == [Token('bar', type='word')] + def test_string_multiline_slash(self): + t = Tokenizer(PythonSchema) + res = list(t.tokenize("'foo\\")) + assert res == [Token("'foo\\", type='string')] + res = list(t.tokenize("bar'")) + assert res == [Token("bar'", type='string')] + res = list(t.tokenize("bar")) + assert res == [Token('bar', type='word')] + def test_string_following_printable(self): assert self.tokens('."foo"') == [Token('.', type='unknown'), Token('"foo"', type='string')] From fijal at codespeak.net Fri Feb 9 19:33:02 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Fri, 9 Feb 2007 19:33:02 +0100 (CET) Subject: [py-svn] r38302 - in py/trunk/py/test: . rsession rsession/testing Message-ID: <20070209183302.137DF1008D@code0.codespeak.net> Author: fijal Date: Fri Feb 9 19:33:00 2007 New Revision: 38302 Modified: py/trunk/py/test/defaultconftest.py py/trunk/py/test/rsession/testing/test_web.py py/trunk/py/test/rsession/testing/test_webjs.py py/trunk/py/test/rsession/web.py Log: Kill _dist_import_pypy Modified: py/trunk/py/test/defaultconftest.py ============================================================================== --- py/trunk/py/test/defaultconftest.py (original) +++ py/trunk/py/test/defaultconftest.py Fri Feb 9 19:33:00 2007 @@ -24,7 +24,6 @@ dist_nicelevel = py.std.os.nice(0) # nice py.test works else: dist_nicelevel = 0 -_dist_import_pypy = False # used for regenerating JS application dist_rsync_ignore = [] # =================================================== Modified: py/trunk/py/test/rsession/testing/test_web.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_web.py (original) +++ py/trunk/py/test/rsession/testing/test_web.py Fri Feb 9 19:33:00 2007 @@ -13,8 +13,6 @@ py.test.skip("PyPy not found") def setup_module(mod): - config = py.test.config._reparse([]) - config._overwrite('_dist_import_pypy', True) from py.__.test.rsession.web import TestHandler as _TestHandler from py.__.test.rsession.web import MultiQueue mod._TestHandler = _TestHandler Modified: py/trunk/py/test/rsession/testing/test_webjs.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_webjs.py (original) +++ py/trunk/py/test/rsession/testing/test_webjs.py Fri Feb 9 19:33:00 2007 @@ -21,8 +21,6 @@ mod.dom = dom dom.window = dom.Window(html) dom.document = dom.window.document - config = py.test.config._reparse([]) - config._overwrite('_dist_import_pypy', True) from py.__.test.rsession import webjs from py.__.test.rsession.web import exported_methods mod.webjs = webjs Modified: py/trunk/py/test/rsession/web.py ============================================================================== --- py/trunk/py/test/rsession/web.py (original) +++ py/trunk/py/test/rsession/web.py Fri Feb 9 19:33:00 2007 @@ -24,11 +24,6 @@ "show_host", "hide_host", "hide_messagebox", "opt_scroll"] try: - try: - if not py.test.config.getvalue('_dist_import_pypy'): - raise ImportError - except AttributeError: - pass from pypy.rpython.ootypesystem.bltregistry import MethodDesc, BasicExternal,\ described from pypy.translator.js.main import rpython2javascript From hpk at codespeak.net Fri Feb 9 20:45:05 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 9 Feb 2007 20:45:05 +0100 (CET) Subject: [py-svn] r38310 - py/trunk/py/apigen/source Message-ID: <20070209194505.A735A1007C@code0.codespeak.net> Author: hpk Date: Fri Feb 9 20:45:04 2007 New Revision: 38310 Modified: py/trunk/py/apigen/source/color.py Log: assertion to be sure about keywords Modified: py/trunk/py/apigen/source/color.py ============================================================================== --- py/trunk/py/apigen/source/color.py (original) +++ py/trunk/py/apigen/source/color.py Fri Feb 9 20:45:04 2007 @@ -14,6 +14,13 @@ 'global', 'import', 'lambda', 'pass', 'print'] linejoin = r'\\' +def assert_keywords(): + from keyword import kwlist + all = PythonSchema.keyword + PythonSchema.alt_keyword + for x in kwlist: + assert x in all +assert_keywords() + class Token(object): data = None type = 'unknown' From hpk at codespeak.net Fri Feb 9 21:36:21 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 9 Feb 2007 21:36:21 +0100 (CET) Subject: [py-svn] r38315 - py/trunk/py/apigen/testing Message-ID: <20070209203621.2334F1008E@code0.codespeak.net> Author: hpk Date: Fri Feb 9 21:36:20 2007 New Revision: 38315 Modified: py/trunk/py/apigen/testing/test_htmlgen.py Log: fixing failing apigen test and adding a string equality assertion helper (if we need to have such exact string-comparison tests, then let's at least make it convenient to discover the problem quickly) Modified: py/trunk/py/apigen/testing/test_htmlgen.py ============================================================================== --- py/trunk/py/apigen/testing/test_htmlgen.py (original) +++ py/trunk/py/apigen/testing/test_htmlgen.py Fri Feb 9 21:36:20 2007 @@ -2,6 +2,26 @@ from py.__.apigen import htmlgen from py.__.apigen.linker import Linker +def assert_eq_string(string1, string2): + if string1 == string2: + return + __tracebackhide__ = True + for i, (c1, c2) in py.builtin.enumerate(zip(string1, string2)): + if c1 != c2: + start = max(0, i-20) + end = i + 20 + py.test.fail("strings not equal in position i=%d\n" + "string1[%d:%d] = %r\n" + "string2[%d:%d] = %r\n" + "string1 = %r\n" + "string2 = %r\n" + % (i, + start, end, string1[start:end], + start, end, string2[start:end], + string1, string2 + )) + + def test_create_namespace_tree(): tree = htmlgen.create_namespace_tree(['foo.bar.baz']) assert tree == {'': ['foo'], @@ -48,7 +68,8 @@ 'ascii') div = py.xml.html.div(*colored).unicode(indent=0) print repr(div) - assert div == (u'
' + assert_eq_string(div, + u'
' '' '' '' @@ -61,7 +82,7 @@ 'def foo():' '' '' '' @@ -73,7 +94,8 @@ 0, 'ascii') div = py.xml.html.div(*colored).unicode(indent=0) print repr(div) - assert div == (u'
' + assert_eq_string (div, + u'
' '
1
' - ' print' + ' print' ' "bar"' '
' '' '' From hpk at codespeak.net Sat Feb 10 09:52:24 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 10 Feb 2007 09:52:24 +0100 (CET) Subject: [py-svn] r38352 - in py/trunk/py/test/rsession: . testing Message-ID: <20070210085224.198001008E@code0.codespeak.net> Author: hpk Date: Sat Feb 10 09:52:22 2007 New Revision: 38352 Modified: py/trunk/py/test/rsession/hostmanage.py py/trunk/py/test/rsession/slave.py py/trunk/py/test/rsession/testing/test_hostmanage.py Log: made localhost inplace handling safer (and more redundant, there is an additional flag now, and host.gw_remotepath is None for localhost-inplace hosts) Modified: py/trunk/py/test/rsession/hostmanage.py ============================================================================== --- py/trunk/py/test/rsession/hostmanage.py (original) +++ py/trunk/py/test/rsession/hostmanage.py Sat Feb 10 09:52:22 2007 @@ -12,14 +12,17 @@ for host """ _hostname2list = {} - localdest = None def __init__(self, spec): parts = spec.split(':', 1) self.hostname = parts.pop(0) self.relpath = parts and parts.pop(0) or "" - if not self.relpath and self.hostname != "localhost": - self.relpath = "pytestcache-%s" % self.hostname + if self.hostname == "localhost" and not self.relpath: + self.inplacelocal = True + else: + self.inplacelocal = False + if not self.relpath: + self.relpath = "pytestcache-%s" % self.hostname assert not parts self.hostid = self._getuniqueid(self.hostname) @@ -35,20 +38,22 @@ else: self.gw = py.execnet.SshGateway(self.hostname, remotepython=python) - relpath = self.relpath or topdir - assert relpath - channel = self.gw.remote_exec(py.code.Source( - gethomedir, - getpath_relto_home, """ - import os - os.chdir(gethomedir()) - path = %r - if path: - path = getpath_relto_home(path) - channel.send(path) - """ % str(relpath) - )) - self.gw_remotepath = channel.receive() + if self.inplacelocal: + self.gw.remote_exec(py.code.Source( + sethomedir, "sethomedir()" + )).waitclose() + self.gw_remotepath = None + else: + relpath = self.relpath or topdir or "" + assert relpath + channel = self.gw.remote_exec(py.code.Source( + gethomedir, + sethomedir, "sethomedir()", + getpath_relto_home, """ + channel.send(getpath_relto_home(%r)) + """ % relpath, + )) + self.gw_remotepath = channel.receive() def __str__(self): return "" % (self.hostname, self.relpath) @@ -88,10 +93,11 @@ def add_target_host(self, host, destrelpath="", reporter=lambda x: None): remotepath = host.gw_remotepath key = host.hostname, host.relpath - if destrelpath: - remotepath = os.path.join(remotepath, destrelpath) - if host.hostname == "localhost" and remotepath == self._sourcedir: + if host.inplacelocal: + remotepath = self._sourcedir self._synced[key] = True + elif destrelpath: + remotepath = os.path.join(remotepath, destrelpath) synced = key in self._synced reporter(repevent.HostRSyncing(host, py.path.local(self._sourcedir), remotepath, synced)) @@ -187,4 +193,11 @@ if not os.path.isabs(targetpath): homedir = gethomedir() targetpath = os.path.join(homedir, targetpath) - return targetpath + return os.path.normpath(targetpath) + +def sethomedir(): + import os + homedir = os.environ.get('HOME', '') + if not homedir: + homedir = os.environ.get('HOMEPATH', '.') + os.chdir(homedir) Modified: py/trunk/py/test/rsession/slave.py ============================================================================== --- py/trunk/py/test/rsession/slave.py (original) +++ py/trunk/py/test/rsession/slave.py Sat Feb 10 09:52:22 2007 @@ -111,7 +111,11 @@ channel = host.gw.remote_exec(str(py.code.Source(setup, "setup()"))) configrepr = config.make_repr(defaultconftestnames) #print "sending configrepr", configrepr - channel.send(host.gw_remotepath) + topdir = host.gw_remotepath + if topdir is None: + assert host.inplacelocal + topdir = config.topdir + channel.send(str(topdir)) channel.send(configrepr) return channel Modified: py/trunk/py/test/rsession/testing/test_hostmanage.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_hostmanage.py (original) +++ py/trunk/py/test/rsession/testing/test_hostmanage.py Sat Feb 10 09:52:22 2007 @@ -4,7 +4,7 @@ import py from py.__.test.rsession.hostmanage import HostRSync, HostInfo, HostManager -from py.__.test.rsession.hostmanage import gethomedir, getpath_relto_home +from py.__.test.rsession.hostmanage import sethomedir, gethomedir, getpath_relto_home from py.__.test.rsession import repevent class DirSetup: @@ -27,11 +27,13 @@ x = HostInfo("localhost:") assert x.hostname == "localhost" assert not x.relpath + assert x.inplacelocal def test_path(self): x = HostInfo("localhost:/tmp") assert x.relpath == "/tmp" assert x.hostname == "localhost" + assert not x.inplacelocal def test_hostid(self): x = HostInfo("localhost:") @@ -115,6 +117,26 @@ assert 'file.txt' in basenames assert 'somedir' in basenames + def test_hrsync_localhost_inplace(self): + h1 = HostInfo("localhost") + events = [] + rsync = HostRSync(self.source) + h1.initgateway() + rsync.add_target_host(h1, reporter=events.append) + assert events + l = [x for x in events + if isinstance(x, repevent.HostRSyncing)] + assert len(l) == 1 + ev = l[0] + assert ev.host == h1 + assert ev.root == ev.remotepath + l = [x for x in events + if isinstance(x, repevent.HostRSyncRootReady)] + assert len(l) == 1 + ev = l[0] + assert ev.root == self.source + assert ev.host == h1 + def test_hrsync_one_host(self): h1 = self._gethostinfo() finished = [] @@ -200,11 +222,24 @@ events = [] hm.init_rsync(events.append) for host in hosts: - assert host.gw_remotepath == str(self.source) + assert host.inplacelocal + assert host.gw_remotepath is None assert not host.relpath assert events def test_getpath_relto_home(): x = getpath_relto_home("hello") assert x == py.path.local._gethomedir().join("hello") + x = getpath_relto_home(".") + assert x == py.path.local._gethomedir() + +def test_sethomedir(): + old = py.path.local.get_temproot().chdir() + try: + sethomedir() + curdir = py.path.local() + finally: + old.chdir() + + assert py.path.local._gethomedir() == curdir From hpk at codespeak.net Sat Feb 10 09:53:56 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 10 Feb 2007 09:53:56 +0100 (CET) Subject: [py-svn] r38353 - py/dist/py Message-ID: <20070210085356.D970F10092@code0.codespeak.net> Author: hpk Date: Sat Feb 10 09:53:55 2007 New Revision: 38353 Removed: py/dist/py/ Log: remove for merge-from-trunk From hpk at codespeak.net Sat Feb 10 09:53:59 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 10 Feb 2007 09:53:59 +0100 (CET) Subject: [py-svn] r38354 - py/dist/py Message-ID: <20070210085359.491291009B@code0.codespeak.net> Author: hpk Date: Sat Feb 10 09:53:57 2007 New Revision: 38354 Added: py/dist/py/ - copied from r38353, py/trunk/py/ Log: use new py-trunk From guido at codespeak.net Sat Feb 10 12:06:59 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Sat, 10 Feb 2007 12:06:59 +0100 (CET) Subject: [py-svn] r38362 - in py/trunk/py/apigen/source: . testing Message-ID: <20070210110659.12F9E1009C@code0.codespeak.net> Author: guido Date: Sat Feb 10 12:06:58 2007 New Revision: 38362 Modified: py/trunk/py/apigen/source/color.py py/trunk/py/apigen/source/testing/test_color.py Log: Fixed stupidity in the last checkin: only matching multi-line strings using slashes if the slash is actually at the end of the line... :( Modified: py/trunk/py/apigen/source/color.py ============================================================================== --- py/trunk/py/apigen/source/color.py (original) +++ py/trunk/py/apigen/source/color.py Sat Feb 10 12:06:58 2007 @@ -71,7 +71,7 @@ j = schema.linejoin for d in schema.string + schema.multiline_string: self._re_strings_multiline.append( - (re.compile('%s.*%s' % (d, j)), + (re.compile('%s.*%s$' % (d, j)), re.compile('.*?%s' % (d,)))) for d in schema.multiline_string: self._re_strings_multiline.append((re.compile('%s.*' % (d,), re.S), Modified: py/trunk/py/apigen/source/testing/test_color.py ============================================================================== --- py/trunk/py/apigen/source/testing/test_color.py (original) +++ py/trunk/py/apigen/source/testing/test_color.py Sat Feb 10 12:06:58 2007 @@ -83,6 +83,8 @@ assert res == [Token("bar'", type='string')] res = list(t.tokenize("bar")) assert res == [Token('bar', type='word')] + res = list(t.tokenize('"foo\\bar"')) + assert res == [Token('"foo\\bar"', type="string")] def test_string_following_printable(self): assert self.tokens('."foo"') == [Token('.', type='unknown'), From guido at codespeak.net Sat Feb 10 13:02:07 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Sat, 10 Feb 2007 13:02:07 +0100 (CET) Subject: [py-svn] r38370 - py/trunk/py/apigen/source Message-ID: <20070210120207.E4A4110095@code0.codespeak.net> Author: guido Date: Sat Feb 10 13:02:06 2007 New Revision: 38370 Modified: py/trunk/py/apigen/source/color.py Log: Hrmph, probably the nastiest case of 'debug print' I ran into so far, bringing apigen almost to a crawl... Also small optimization making that 'normal' multi-line strings are resolved before those using \. Modified: py/trunk/py/apigen/source/color.py ============================================================================== --- py/trunk/py/apigen/source/color.py (original) +++ py/trunk/py/apigen/source/color.py Sat Feb 10 13:02:06 2007 @@ -67,15 +67,15 @@ self._re_strings_full.append( re.compile(r'%s[^\\%s]+(\\.[^\\%s]*)*%s' % (d, d, d, d))) self._re_strings_empty.append(re.compile('%s%s' % (d, d))) + for d in schema.multiline_string: + self._re_strings_multiline.append((re.compile('%s.*' % (d,), re.S), + re.compile('.*?%s' % (d,)))) if schema.linejoin: j = schema.linejoin - for d in schema.string + schema.multiline_string: + for d in schema.string: self._re_strings_multiline.append( (re.compile('%s.*%s$' % (d, j)), re.compile('.*?%s' % (d,)))) - for d in schema.multiline_string: - self._re_strings_multiline.append((re.compile('%s.*' % (d,), re.S), - re.compile('.*?%s' % (d,)))) # no multi-line comments in Python... phew :) self._re_comments = [] for start, end in schema.comment: @@ -119,7 +119,6 @@ def _check_multiline_strings(self, data): token = None for start, end in self._re_strings_multiline: - print dir(start), end m = start.match(data) if m: s = m.group(0) From hpk at codespeak.net Sat Feb 10 14:21:28 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 10 Feb 2007 14:21:28 +0100 (CET) Subject: [py-svn] r38378 - in py/trunk/py/test/rsession: . testing Message-ID: <20070210132128.38E5410081@code0.codespeak.net> Author: hpk Date: Sat Feb 10 14:21:26 2007 New Revision: 38378 Modified: py/trunk/py/test/rsession/hostmanage.py py/trunk/py/test/rsession/testing/test_hostmanage.py Log: snapshot commit for: each rsync root is now copied to remote-topdir.join(root.basename) Modified: py/trunk/py/test/rsession/hostmanage.py ============================================================================== --- py/trunk/py/test/rsession/hostmanage.py (original) +++ py/trunk/py/test/rsession/hostmanage.py Sat Feb 10 14:21:26 2007 @@ -139,7 +139,10 @@ for root in self.roots: rsync = HostRSync(root, ignores=ignores, verbose=self.config.option.verbose) - destrelpath = root.relto(self.config.topdir) + if root == self.config.topdir: + destrelpath ="" + else: + destrelpath = root.basename for host in self.hosts: rsync.add_target_host(host, destrelpath, reporter) rsync.send(raises=False) Modified: py/trunk/py/test/rsession/testing/test_hostmanage.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_hostmanage.py (original) +++ py/trunk/py/test/rsession/testing/test_hostmanage.py Sat Feb 10 14:21:26 2007 @@ -170,6 +170,7 @@ dir2 = self.source.ensure("dir1", "dir2", dir=1) dir2.ensure("hello") config = py.test.config._reparse([self.source]) + assert config.topdir == self.source hm = HostManager(config, hosts=[HostInfo("localhost:" + str(self.dest))]) events = [] @@ -191,11 +192,9 @@ hosts=[HostInfo("localhost:" + str(self.dest))]) events = [] hm.init_rsync(reporter=events.append) - assert self.dest.join("dir1").check() - assert self.dest.join("dir1", "dir2").check() - assert self.dest.join("dir1", "dir2", 'hello').check() + assert self.dest.join("dir2").check() + assert not self.dest.join("dir1").check() assert not self.dest.join("bogus").check() - assert not self.dest.join("dir1", "somefile").check() def test_hostmanager_rsync_ignore(self): dir2 = self.source.ensure("dir1", "dir2", dir=1) @@ -209,6 +208,7 @@ hm = HostManager(config, hosts=[HostInfo("localhost:" + str(self.dest))]) events = [] + print events hm.init_rsync(reporter=events.append) assert self.dest.join("dir1").check() assert not self.dest.join("dir1", "dir2").check() From guido at codespeak.net Sat Feb 10 15:20:22 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Sat, 10 Feb 2007 15:20:22 +0100 (CET) Subject: [py-svn] r38386 - in py/trunk/py/apigen/source: . testing Message-ID: <20070210142022.8C4981007E@code0.codespeak.net> Author: guido Date: Sat Feb 10 15:20:21 2007 New Revision: 38386 Modified: py/trunk/py/apigen/source/color.py py/trunk/py/apigen/source/testing/test_color.py Log: Fixed some more nasty problems after running py.test --apigen on pypy: problem recognizing """foo "bar" baz""" as a single line string (hack unfortunately), unicode problems. Also added some code to debug the tokenizer: you can now run the color.py script with a Python file as arg to tokenize the file. Modified: py/trunk/py/apigen/source/color.py ============================================================================== --- py/trunk/py/apigen/source/color.py (original) +++ py/trunk/py/apigen/source/color.py Sat Feb 10 15:20:21 2007 @@ -44,10 +44,11 @@ very naive tokenizer, state is recorded for multi-line strings, etc. """ - _re_word = re.compile('[\w_]+') - _re_space = re.compile('\s+') - _re_number = re.compile('[\d\.]*\d[\d\.]*l?', re.I) - _re_rest = re.compile('[^\w\s\d\'"]+') # XXX cheating a bit with the quotes + _re_word = re.compile('[\w_]+', re.U) + _re_space = re.compile('\s+', re.U) + _re_number = re.compile('[\d\.]*\d[\d\.]*l?', re.I | re.U) + # XXX cheating a bit with the quotes + _re_rest = re.compile('[^\w\s\d\'"]+', re.U) # these will be filled using the schema _re_strings_full = None @@ -68,13 +69,14 @@ re.compile(r'%s[^\\%s]+(\\.[^\\%s]*)*%s' % (d, d, d, d))) self._re_strings_empty.append(re.compile('%s%s' % (d, d))) for d in schema.multiline_string: - self._re_strings_multiline.append((re.compile('%s.*' % (d,), re.S), + self._re_strings_multiline.append((re.compile('(%s).*' % (d,), + re.S), re.compile('.*?%s' % (d,)))) if schema.linejoin: j = schema.linejoin for d in schema.string: self._re_strings_multiline.append( - (re.compile('%s.*%s$' % (d, j)), + (re.compile('(%s).*%s$' % (d, j)), re.compile('.*?%s' % (d,)))) # no multi-line comments in Python... phew :) self._re_comments = [] @@ -123,7 +125,15 @@ if m: s = m.group(0) data = '' - self._inside_multiline = end + # XXX take care of a problem which is hard to fix with regexps: + # '''foo 'bar' baz''' will not match single-line strings + # (because [^"""] matches just a single " already), so let's + # try to catch it here... (quite Python specific issue!) + endm = end.match(s[len(m.group(1)):]) + if endm: # see if it ends here already + s = m.group(1) + endm.group(0) + else: + self._inside_multiline = end token = Token(s, 'string') break return data, token @@ -184,4 +194,18 @@ return data[len(s):], Token(s, 'unknown') return data, None +if __name__ == '__main__': + import py, sys + if len(sys.argv) != 2: + print 'usage: %s ' + print ' tokenizes the file and prints the tokens per line' + sys.exit(1) + t = Tokenizer(PythonSchema) + p = py.path.local(sys.argv[1]) + assert p.ext == '.py' + for line in p.read().split('\n'): + print repr(line) + print 't in multiline mode:', not not t._inside_multiline + tokens = t.tokenize(line) + print list(tokens) Modified: py/trunk/py/apigen/source/testing/test_color.py ============================================================================== --- py/trunk/py/apigen/source/testing/test_color.py (original) +++ py/trunk/py/apigen/source/testing/test_color.py Sat Feb 10 15:20:21 2007 @@ -90,3 +90,8 @@ assert self.tokens('."foo"') == [Token('.', type='unknown'), Token('"foo"', type='string')] + def test_something_strange(self): + t = Tokenizer(PythonSchema) + tokens = list(t.tokenize('"""foo "bar" baz"""')) + assert not t._inside_multiline + From guido at codespeak.net Sat Feb 10 15:22:17 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Sat, 10 Feb 2007 15:22:17 +0100 (CET) Subject: [py-svn] r38388 - py/trunk/py/apigen Message-ID: <20070210142217.621C41008D@code0.codespeak.net> Author: guido Date: Sat Feb 10 15:22:16 2007 New Revision: 38388 Modified: py/trunk/py/apigen/todo-apigen.txt Log: Removed stuff that's done, added a couple more items to do. Modified: py/trunk/py/apigen/todo-apigen.txt ============================================================================== --- py/trunk/py/apigen/todo-apigen.txt (original) +++ py/trunk/py/apigen/todo-apigen.txt Sat Feb 10 15:22:16 2007 @@ -1,23 +1,4 @@ -* format docstrings more nicely (with tests) - - DONE I guess - -* have the API function view be as informative as possible - without having to go to the "single method" view - (do we even need a single method view?), for example: - - browsing the class views (and clicking on methods) - should always make it obvious which class is being - viewed. method views (when navigating there through - the class view) should also have the source there - - DONE I think, single method view is gone - -* have class-level attributes be displayed - - DONE - * use "inherited" doc strings, i.e. for class A: def meth(self): @@ -31,68 +12,7 @@ NOT YET DONE (later?) -* factor out some common code in the build_* functions - - (mostly) DONE - -* refactor the apigen/rsession interaction to become - cleaner (e.g. apigen's get_documentable_items should - be separately tested and the caller should not need - to guess what it will get, i think) - - DONE - -* look out for and streamline all apigen/source-viewer - documentation into one document - -* consider automating dependencies: - - e.g. something like: queue_render(page, fspath, linker, ...) - would defer the rendering until later. - then a loop does: - - maxlength = len(queue) - while queue: - page, fspath, linker, ... = queue.get() - # fill outputpath/link here or even earlier - if all_links_resolve(page, linker): - render it and write to filesystem - maxlength = len(queue) - else: - queue.append(...) - maxlength -= 1 - if maxlength <= 0: - print "ERROR: seems i can't make progress" - print "unresolved links follow: " - ... - print "unresolved pages/fspaths:" - ... - XXX maybe: print "filling linker with dummy hrefs, and rendering anyway" - ... - raise ... - - NOT SURE if this is still required - -* also we might have a support function for tests that - fills the linker with "dummy hrefs" for certain types - like source links - - KIND OF DONE, the tests now use a linker that just doesn't - barf on non-existing linkids anymore, which seems to be - good enough (we may want to add more sophisticated debugging - later, but for now this works) - -* add syntax coloring for Python source snippets - - DONE - -* remove py.test/apigen cruft from stack traces - - DONE, thanks to fijal - -* fix non-ascii source encoding support - - DONE +* add SVN info to the (source only?) pages -* XXX +* add warning about py.test possibly not covering the whole API From hpk at codespeak.net Sat Feb 10 15:45:42 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 10 Feb 2007 15:45:42 +0100 (CET) Subject: [py-svn] r38389 - in py/trunk/py/test/rsession: . testing Message-ID: <20070210144542.A89921008E@code0.codespeak.net> Author: hpk Date: Sat Feb 10 15:45:41 2007 New Revision: 38389 Modified: py/trunk/py/test/rsession/hostmanage.py py/trunk/py/test/rsession/testing/test_hostmanage.py Log: completing the picture: now if you don't have rsync_roots specified, the config.topdir is transfered but it is transferred to the "remotepath.join(topdir.basename)" (not actual code) to avoid random such rsyncs to destroy/affect remote filesystem state. Modified: py/trunk/py/test/rsession/hostmanage.py ============================================================================== --- py/trunk/py/test/rsession/hostmanage.py (original) +++ py/trunk/py/test/rsession/hostmanage.py Sat Feb 10 15:45:41 2007 @@ -13,17 +13,23 @@ """ _hostname2list = {} - def __init__(self, spec): + def __init__(self, spec, addrel=""): parts = spec.split(':', 1) self.hostname = parts.pop(0) self.relpath = parts and parts.pop(0) or "" if self.hostname == "localhost" and not self.relpath: self.inplacelocal = True + if addrel: + raise ValueError("inplace localhosts cannot have " + "additional path %r" % addrel) else: self.inplacelocal = False if not self.relpath: self.relpath = "pytestcache-%s" % self.hostname + if addrel: + self.relpath += "/" + addrel # XXX too os-dependent assert not parts + assert self.inplacelocal or self.relpath self.hostid = self._getuniqueid(self.hostname) def _getuniqueid(self, hostname): @@ -32,7 +38,7 @@ l.append(hostid) return hostid - def initgateway(self, python="python", topdir=None): + def initgateway(self, python="python"): if self.hostname == "localhost": self.gw = py.execnet.PopenGateway(python=python) else: @@ -44,14 +50,13 @@ )).waitclose() self.gw_remotepath = None else: - relpath = self.relpath or topdir or "" - assert relpath + assert self.relpath channel = self.gw.remote_exec(py.code.Source( gethomedir, sethomedir, "sethomedir()", getpath_relto_home, """ channel.send(getpath_relto_home(%r)) - """ % relpath, + """ % self.relpath, )) self.gw_remotepath = channel.receive() @@ -116,19 +121,21 @@ class HostManager(object): def __init__(self, config, hosts=None): self.config = config - if hosts is None: - hosts = self.config.getvalue("dist_hosts") - hosts = [HostInfo(x) for x in hosts] - self.hosts = hosts roots = self.config.getvalue_pathlist("dist_rsync_roots") + addrel = "" if roots is None: roots = [self.config.topdir] + addrel = self.config.topdir.basename self.roots = roots + if hosts is None: + hosts = self.config.getvalue("dist_hosts") + hosts = [HostInfo(x, addrel) for x in hosts] + self.hosts = hosts def prepare_gateways(self, reporter): python = self.config.getvalue("dist_remotepython") for host in self.hosts: - host.initgateway(python=python, topdir=self.config.topdir) + host.initgateway(python=python) reporter(repevent.HostGatewayReady(host, self.roots)) host.gw.host = host @@ -140,7 +147,7 @@ rsync = HostRSync(root, ignores=ignores, verbose=self.config.option.verbose) if root == self.config.topdir: - destrelpath ="" + destrelpath = "" else: destrelpath = root.basename for host in self.hosts: Modified: py/trunk/py/test/rsession/testing/test_hostmanage.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_hostmanage.py (original) +++ py/trunk/py/test/rsession/testing/test_hostmanage.py Sat Feb 10 15:45:41 2007 @@ -27,7 +27,15 @@ x = HostInfo("localhost:") assert x.hostname == "localhost" assert not x.relpath - assert x.inplacelocal + + def test_addrel(self): + py.test.raises(ValueError, """ + HostInfo("localhost:", addrel="whatever") + """) + host = HostInfo("localhost:/tmp", addrel="base") + assert host.relpath == "/tmp/base" + host = HostInfo("localhost:tmp", addrel="base2") + assert host.relpath == "tmp/base2" def test_path(self): x = HostInfo("localhost:/tmp") @@ -161,6 +169,16 @@ assert not res2 class TestHostManager(DirSetup): + def gethostmanager(self, dist_hosts): + self.source.join("conftest.py").write(py.code.Source(""" + dist_hosts = %r + """ % (dist_hosts,))) + config = py.test.config._reparse([self.source]) + assert config.topdir == self.source + hm = HostManager(config) + assert hm.hosts + return hm + def test_hostmanager_custom_hosts(self): config = py.test.config._reparse([self.source]) hm = HostManager(config, hosts=[1,2,3]) @@ -169,15 +187,15 @@ def test_hostmanager_init_rsync_topdir(self): dir2 = self.source.ensure("dir1", "dir2", dir=1) dir2.ensure("hello") - config = py.test.config._reparse([self.source]) - assert config.topdir == self.source - hm = HostManager(config, - hosts=[HostInfo("localhost:" + str(self.dest))]) - events = [] - hm.init_rsync(reporter=events.append) - assert self.dest.join("dir1").check() - assert self.dest.join("dir1", "dir2").check() - assert self.dest.join("dir1", "dir2", 'hello').check() + hm = self.gethostmanager( + dist_hosts = ["localhost:%s" % self.dest] + ) + assert hm.config.topdir == self.source + hm.init_rsync([].append) + dest = self.dest.join(self.source.basename) + assert dest.join("dir1").check() + assert dest.join("dir1", "dir2").check() + assert dest.join("dir1", "dir2", 'hello').check() def test_hostmanager_init_rsync_rsync_roots(self): dir2 = self.source.ensure("dir1", "dir2", dir=1) From hpk at codespeak.net Sat Feb 10 15:51:25 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 10 Feb 2007 15:51:25 +0100 (CET) Subject: [py-svn] r38390 - in py/trunk/py/test/rsession: . testing Message-ID: <20070210145125.01D9510095@code0.codespeak.net> Author: hpk Date: Sat Feb 10 15:51:25 2007 New Revision: 38390 Modified: py/trunk/py/test/rsession/hostmanage.py py/trunk/py/test/rsession/testing/test_hostmanage.py Log: refining the logic: for inplace-localhosts ("optimized localhost") we do not add any relative path. Modified: py/trunk/py/test/rsession/hostmanage.py ============================================================================== --- py/trunk/py/test/rsession/hostmanage.py (original) +++ py/trunk/py/test/rsession/hostmanage.py Sat Feb 10 15:51:25 2007 @@ -19,9 +19,7 @@ self.relpath = parts and parts.pop(0) or "" if self.hostname == "localhost" and not self.relpath: self.inplacelocal = True - if addrel: - raise ValueError("inplace localhosts cannot have " - "additional path %r" % addrel) + addrel = "" # inplace localhosts cannot have additions else: self.inplacelocal = False if not self.relpath: Modified: py/trunk/py/test/rsession/testing/test_hostmanage.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_hostmanage.py (original) +++ py/trunk/py/test/rsession/testing/test_hostmanage.py Sat Feb 10 15:51:25 2007 @@ -29,9 +29,9 @@ assert not x.relpath def test_addrel(self): - py.test.raises(ValueError, """ - HostInfo("localhost:", addrel="whatever") - """) + host = HostInfo("localhost:", addrel="whatever") + assert host.inplacelocal + assert not host.relpath host = HostInfo("localhost:/tmp", addrel="base") assert host.relpath == "/tmp/base" host = HostInfo("localhost:tmp", addrel="base2") From hpk at codespeak.net Sat Feb 10 16:05:34 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 10 Feb 2007 16:05:34 +0100 (CET) Subject: [py-svn] r38391 - in py/trunk/py/test/rsession: . testing Message-ID: <20070210150534.22B2410094@code0.codespeak.net> Author: hpk Date: Sat Feb 10 16:05:32 2007 New Revision: 38391 Modified: py/trunk/py/test/rsession/hostmanage.py py/trunk/py/test/rsession/testing/test_hostmanage.py Log: check more exactly if we are working from a default rsync_root (checking for rsync_root == topdir is not safe as the topdir may be explicitely contained in the rsync_root list) Modified: py/trunk/py/test/rsession/hostmanage.py ============================================================================== --- py/trunk/py/test/rsession/hostmanage.py (original) +++ py/trunk/py/test/rsession/hostmanage.py Sat Feb 10 16:05:32 2007 @@ -124,6 +124,7 @@ if roots is None: roots = [self.config.topdir] addrel = self.config.topdir.basename + self._addrel = addrel self.roots = roots if hosts is None: hosts = self.config.getvalue("dist_hosts") @@ -144,7 +145,7 @@ for root in self.roots: rsync = HostRSync(root, ignores=ignores, verbose=self.config.option.verbose) - if root == self.config.topdir: + if self._addrel: destrelpath = "" else: destrelpath = root.basename Modified: py/trunk/py/test/rsession/testing/test_hostmanage.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_hostmanage.py (original) +++ py/trunk/py/test/rsession/testing/test_hostmanage.py Sat Feb 10 16:05:32 2007 @@ -169,10 +169,11 @@ assert not res2 class TestHostManager(DirSetup): - def gethostmanager(self, dist_hosts): - self.source.join("conftest.py").write(py.code.Source(""" - dist_hosts = %r - """ % (dist_hosts,))) + def gethostmanager(self, dist_hosts, dist_rsync_roots=None): + l = ["dist_hosts = %r" % dist_hosts] + if dist_rsync_roots: + l.append("dist_rsync_roots = %r" % dist_rsync_roots) + self.source.join("conftest.py").write("\n".join(l)) config = py.test.config._reparse([self.source]) assert config.topdir == self.source hm = HostManager(config) @@ -197,7 +198,21 @@ assert dest.join("dir1", "dir2").check() assert dest.join("dir1", "dir2", 'hello').check() - def test_hostmanager_init_rsync_rsync_roots(self): + def test_hostmanager_init_rsync_topdir_explicit(self): + dir2 = self.source.ensure("dir1", "dir2", dir=1) + dir2.ensure("hello") + hm = self.gethostmanager( + dist_hosts = ["localhost:%s" % self.dest] + dist_rsync_roots = [self.source] + ) + assert hm.config.topdir == self.source + hm.init_rsync([].append) + dest = self.dest.join(self.source.basename) + assert dest.join("dir1").check() + assert dest.join("dir1", "dir2").check() + assert dest.join("dir1", "dir2", 'hello').check() + + def test_hostmanager_init_rsync_roots(self): dir2 = self.source.ensure("dir1", "dir2", dir=1) self.source.ensure("dir1", "somefile", dir=1) dir2.ensure("hello") From hpk at codespeak.net Sat Feb 10 16:07:30 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 10 Feb 2007 16:07:30 +0100 (CET) Subject: [py-svn] r38392 - py/trunk/py/test/rsession/testing Message-ID: <20070210150730.F347910094@code0.codespeak.net> Author: hpk Date: Sat Feb 10 16:07:30 2007 New Revision: 38392 Modified: py/trunk/py/test/rsession/testing/test_hostmanage.py Log: looponfailing tricked me into committing too early (syntax error, bah) Modified: py/trunk/py/test/rsession/testing/test_hostmanage.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_hostmanage.py (original) +++ py/trunk/py/test/rsession/testing/test_hostmanage.py Sat Feb 10 16:07:30 2007 @@ -202,8 +202,8 @@ dir2 = self.source.ensure("dir1", "dir2", dir=1) dir2.ensure("hello") hm = self.gethostmanager( - dist_hosts = ["localhost:%s" % self.dest] - dist_rsync_roots = [self.source] + dist_hosts = ["localhost:%s" % self.dest], + dist_rsync_roots = [str(self.source)] ) assert hm.config.topdir == self.source hm.init_rsync([].append) From guido at codespeak.net Sat Feb 10 16:19:19 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Sat, 10 Feb 2007 16:19:19 +0100 (CET) Subject: [py-svn] r38393 - in py/trunk/py/apigen: . testing Message-ID: <20070210151919.88BA11009A@code0.codespeak.net> Author: guido Date: Sat Feb 10 16:19:17 2007 New Revision: 38393 Modified: py/trunk/py/apigen/htmlgen.py py/trunk/py/apigen/testing/test_apigen_example.py py/trunk/py/apigen/testing/test_htmlgen.py Log: Made (displayed) paths to source files relative whenever possible. Modified: py/trunk/py/apigen/htmlgen.py ============================================================================== --- py/trunk/py/apigen/htmlgen.py (original) +++ py/trunk/py/apigen/htmlgen.py Sat Feb 10 16:19:17 2007 @@ -153,6 +153,12 @@ full_dotted_name)) return ret +def get_rel_sourcepath(projpath, filename, default=None): + relpath = py.path.local(filename).relto(projpath) + if not relpath: + return default + return relpath + # the PageBuilder classes take care of producing the docs (using the stuff # above) class AbstractPageBuilder(object): @@ -339,7 +345,8 @@ sep = get_linesep(callable_source) org = callable_source.split(sep) colored = [enumerate_and_color(org, firstlineno, enc)] - text = 'source: %s' % (sourcefile,) + relpath = get_rel_sourcepath(self.projroot, sourcefile, sourcefile) + text = 'source: %s' % (relpath,) if is_in_pkg: href = self.linker.get_lazyhref(sourcefile) @@ -606,8 +613,9 @@ href = self.linker.get_lazyhref(id) self.write_page('call site %s for %s' % (index, dotted_name), reltargetpath, tbtag, nav) - return H.CallStackLink(call_site[0].filename, call_site[0].lineno + 1, - href) + sourcefile = call_site[0].filename + sourcepath = get_rel_sourcepath(self.projpath, sourcefile, sourcefile) + return H.CallStackLink(sourcepath, call_site[0].lineno + 1, href) _reg_source = py.std.re.compile(r'([^>]*)<(.*)>') def gen_traceback(self, dotted_name, call_site): @@ -629,7 +637,9 @@ l = ' %s' % (sline,) mangled.append(l) if sourcefile: - linktext = '%s - line %s' % (sourcefile, frame.lineno + 1) + relpath = get_rel_sourcepath(self.projpath, sourcefile, + sourcefile) + linktext = '%s - line %s' % (relpath, frame.lineno + 1) # skip py.code.Source objects and source files outside of the # package is_code_source = self._reg_source.match(sourcefile) Modified: py/trunk/py/apigen/testing/test_apigen_example.py ============================================================================== --- py/trunk/py/apigen/testing/test_apigen_example.py (original) +++ py/trunk/py/apigen/testing/test_apigen_example.py Sat Feb 10 16:19:17 2007 @@ -144,8 +144,10 @@ assert pos5 > pos4 and pos5 > pos3 pos6 = html.find('<None>', pos5) assert pos6 > pos5 - pos7 = html.find('source: %s' % (self.fs_root.join('pkg/func.py'),), - pos6) + sourcefile = self.fs_root.join('pkg/func.py') + pos7 = html.find('source: %s' % (get_rel_sourcepath(apb.projpath, + sourcefile),), + pos6) assert pos7 > pos6 _checkhtmlsnippet(html) Modified: py/trunk/py/apigen/testing/test_htmlgen.py ============================================================================== --- py/trunk/py/apigen/testing/test_htmlgen.py (original) +++ py/trunk/py/apigen/testing/test_htmlgen.py Sat Feb 10 16:19:17 2007 @@ -20,8 +20,7 @@ start, end, string2[start:end], string1, string2 )) - - + def test_create_namespace_tree(): tree = htmlgen.create_namespace_tree(['foo.bar.baz']) assert tree == {'': ['foo'], @@ -127,3 +126,12 @@ assert not htmlgen.show_property('__name__') assert not htmlgen.show_property('__class__') +def test_get_rel_sourcepath(): + projpath = py.path.local('/proj') + assert (htmlgen.get_rel_sourcepath(projpath, py.path.local('/proj/foo')) == + 'foo') + assert (htmlgen.get_rel_sourcepath(projpath, py.path.local('/foo')) is + None) + assert (htmlgen.get_rel_sourcepath(projpath, py.path.local('')) is + None) + From guido at codespeak.net Sat Feb 10 16:41:32 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Sat, 10 Feb 2007 16:41:32 +0100 (CET) Subject: [py-svn] r38396 - in py/trunk/py: bin misc/testing Message-ID: <20070210154132.665E210079@code0.codespeak.net> Author: guido Date: Sat Feb 10 16:41:31 2007 New Revision: 38396 Modified: py/trunk/py/bin/_update_website.py py/trunk/py/misc/testing/test_update_website.py Log: Capturing stdout and stderr when running the update_website tests to not mess up --rest output. Modified: py/trunk/py/bin/_update_website.py ============================================================================== --- py/trunk/py/bin/_update_website.py (original) +++ py/trunk/py/bin/_update_website.py Sat Feb 10 16:41:31 2007 @@ -22,7 +22,7 @@ rs.add_target(gateway, remotepath, delete=True) rs.send() -def run_tests(pkgpath, args=''): +def run_tests(pkgpath, args='', captureouterr=False): """ run the unit tests and build the docs """ pypath = py.__package__.getpath() pytestpath = pypath.join('bin/py.test') @@ -32,13 +32,16 @@ apigenpath = pkgpath.join('apigen/apigen.py') # XXX be more general here? if not apigenpath.check(file=True): apigenpath = pypath.join('apigen/apigen.py') - cmd = 'PYTHONPATH="%s:%s" python "%s" --apigen="%s" "%s" %s' % ( + cmd = 'PYTHONPATH="%s:%s" python "%s" %s --apigen="%s" "%s"' % ( pypath.dirpath(), pkgpath.dirpath(), pytestpath, + args, apigenpath, pkgpath, - args) + ) + if captureouterr: + cmd += ' > /dev/null 2>&1' status = py.std.os.system(cmd) return status Modified: py/trunk/py/misc/testing/test_update_website.py ============================================================================== --- py/trunk/py/misc/testing/test_update_website.py (original) +++ py/trunk/py/misc/testing/test_update_website.py Sat Feb 10 16:41:31 2007 @@ -52,7 +52,7 @@ if py.std.sys.platform == "win32": py.test.skip("update_website is not supposed to be run from win32") pkgpath = setup_pkg('update_website_run_tests') - errors = update_website.run_tests(pkgpath) + errors = update_website.run_tests(pkgpath, captureouterr=True) assert not errors assert pkgpath.join('../apigen').check(dir=True) assert pkgpath.join('../apigen/api/sub.foo.html').check(file=True) @@ -63,6 +63,6 @@ pkgpath = setup_pkg('update_website_run_tests_failure') assert not pkgpath.join('../apigen').check(dir=True) pkgpath.ensure('../apigen', file=True) - errors = update_website.run_tests(pkgpath, '> /dev/null 2>&1') + errors = update_website.run_tests(pkgpath, captureouterr=True) assert errors # some error message From fijal at codespeak.net Sat Feb 10 17:50:50 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Sat, 10 Feb 2007 17:50:50 +0100 (CET) Subject: [py-svn] r38397 - in py/trunk/py/test/rsession: . testing Message-ID: <20070210165050.551661008E@code0.codespeak.net> Author: fijal Date: Sat Feb 10 17:50:47 2007 New Revision: 38397 Modified: py/trunk/py/test/rsession/executor.py py/trunk/py/test/rsession/local.py py/trunk/py/test/rsession/outcome.py py/trunk/py/test/rsession/slave.py py/trunk/py/test/rsession/testing/test_executor.py py/trunk/py/test/rsession/testing/test_master.py py/trunk/py/test/rsession/testing/test_slave.py Log: Kill PidInfo and make boxing optional for distributed testing. This should make dist testing on windows possible. Modified: py/trunk/py/test/rsession/executor.py ============================================================================== --- py/trunk/py/test/rsession/executor.py (original) +++ py/trunk/py/test/rsession/executor.py Sat Feb 10 17:50:47 2007 @@ -20,12 +20,19 @@ self.config = config assert self.config - def run(self): - self.item.run() + def run(self, capture=True): + if capture: + try: + self.item.startcapture() + self.item.run() + finally: + self.item.finishcapture() + else: + self.item.run() - def execute(self): + def execute(self, capture=True): try: - self.run() + self.run(capture) outcome = Outcome() except Skipped, e: outcome = Outcome(skipped=str(e)) @@ -49,8 +56,7 @@ # XXX hmm, we probably will not like to continue from that # point raise SystemExit() - outcome.stdout = "" - outcome.stderr = "" + outcome.stdout, outcome.stderr = self.item._getouterr() return outcome class ApigenExecutor(RunExecutor): @@ -68,7 +74,7 @@ finally: self.tracer.end_tracing() - def run(self): + def run(self, capture): """ We want to trace *only* function objects here. Unsure what to do with custom collectors at all """ @@ -83,7 +89,7 @@ def execute(self): def fun(): - outcome = RunExecutor.execute(self) + outcome = RunExecutor.execute(self, False) return outcome.make_repr(self.config.option.tbstyle) b = Box(fun, config=self.config) pid = b.run() @@ -105,7 +111,7 @@ def execute(self): def fun(): - outcome = RunExecutor.execute(self) + outcome = RunExecutor.execute(self, False) return outcome.make_repr(self.config.option.tbstyle) b = Box(fun, config=self.config) Modified: py/trunk/py/test/rsession/local.py ============================================================================== --- py/trunk/py/test/rsession/local.py (original) +++ py/trunk/py/test/rsession/local.py Sat Feb 10 17:50:47 2007 @@ -8,29 +8,14 @@ from py.__.test.rsession import repevent from py.__.test.rsession.outcome import ReprOutcome -# XXX copied from session.py -def startcapture(session): - if not session.config.option.nocapture: - session._capture = py.io.StdCapture() - -def finishcapture(session): - if hasattr(session, '_capture'): - capture = session._capture - del session._capture - return capture.reset() - return "", "" - def box_runner(item, session, reporter): r = BoxExecutor(item, config=session.config) return ReprOutcome(r.execute()) def plain_runner(item, session, reporter): - # box executor is doing stdout/err catching for us, let's do it here - startcapture(session) r = RunExecutor(item, usepdb=session.config.option.usepdb, reporter=reporter, config=session.config) outcome = r.execute() outcome = ReprOutcome(outcome.make_repr(session.config.option.tbstyle)) - outcome.stdout, outcome.stderr = finishcapture(session) return outcome def benchmark_runner(item, session, reporter): Modified: py/trunk/py/test/rsession/outcome.py ============================================================================== --- py/trunk/py/test/rsession/outcome.py (original) +++ py/trunk/py/test/rsession/outcome.py Sat Feb 10 17:50:47 2007 @@ -18,6 +18,8 @@ self.excinfo = excinfo self.is_critical = is_critical self.signal = 0 + self.stdout = "" # XXX temporary + self.stderr = "" assert bool(self.passed) + bool(excinfo) + bool(skipped) == 1 def make_excinfo_repr(self, tbstyle): @@ -54,7 +56,7 @@ def make_repr(self, tbstyle="long"): return (self.passed, self.setupfailure, self.make_excinfo_repr(tbstyle), - self.skipped, self.is_critical, 0, "", "") + self.skipped, self.is_critical, 0, self.stdout, self.stderr) class TracebackEntryRepr(object): def __init__(self, tbentry): Modified: py/trunk/py/test/rsession/slave.py ============================================================================== --- py/trunk/py/test/rsession/slave.py (original) +++ py/trunk/py/test/rsession/slave.py Sat Feb 10 17:50:47 2007 @@ -9,59 +9,16 @@ import thread import os -class PidInfo(object): - """ Pure container class to store information of actually running - pid - """ - def __init__(self): - self.pid = 0 - self.lock = thread.allocate_lock() - - def set_pid(self, pid): - self.lock.acquire() - try: - self.pid = pid - finally: - self.lock.release() - - def kill(self): - self.lock.acquire() - try: - if self.pid: - os.kill(self.pid, 15) - self.pid = 0 - finally: - self.lock.release() - - def waitandclear(self, pid, num): - """ This is an obscure hack to keep locking properly, adhere to posix semantics - and try to clean it as much as possible, not clean at all - """ - self.lock.acquire() - try: - retval = os.waitpid(self.pid, 0) - self.pid = 0 - return retval - finally: - self.lock.release() - class SlaveNode(object): - def __init__(self, config, pidinfo, executor=AsyncExecutor): + def __init__(self, config, executor): #self.rootcollector = rootcollector self.config = config self.executor = executor - self.pidinfo = pidinfo def execute(self, itemspec): item = self.config._getcollector(itemspec) ex = self.executor(item, config=self.config) - if self.executor is AsyncExecutor: - cont, pid = ex.execute() - self.pidinfo.set_pid(pid) - else: - # for tests only - return ex.execute() - return cont(self.pidinfo.waitandclear) + return ex.execute() def run(self, itemspec): #outcome = self.execute(itemspec) @@ -72,7 +29,7 @@ else: return outcome.make_repr(self.config.option.tbstyle) -def slave_main(receive, send, path, config, pidinfo): +def slave_main(receive, send, path, config): import os assert os.path.exists(path) path = os.path.abspath(path) @@ -82,7 +39,11 @@ if node is not None: return node col = py.test.collect.Directory(str(py.path.local(path).join(item[0]))) - node = nodes[item[0]] = SlaveNode(config, pidinfo) + if config.option.boxed: + executor = BoxExecutor + else: + executor = RunExecutor + node = nodes[item[0]] = SlaveNode(config, executor) return node while 1: nextitem = receive() @@ -120,15 +81,6 @@ return channel def setup(): - def callback_gen(channel, queue, info): - def callback(item): - if item == 42: # magic call-cleanup - # XXX should kill a pid here - info.kill() - channel.close() - sys.exit(0) - queue.put(item) - return callback # our current dir is the topdir import os, sys basedir = channel.receive() @@ -139,13 +91,13 @@ config = py.test.config assert not config._initialized config.initdirect(basedir, config_repr) + if hasattr(os, 'nice'): + nice_level = config.getvalue('dist_nicelevel') + os.nice(nice_level) if not config.option.nomagic: py.magic.invoke(assertion=1) - from py.__.test.rsession.slave import slave_main, PidInfo - queue = py.std.Queue.Queue() - pidinfo = PidInfo() - channel.setcallback(callback_gen(channel, queue, pidinfo)) - slave_main(queue.get, channel.send, basedir, config, pidinfo) + from py.__.test.rsession.slave import slave_main + slave_main(channel.receive, channel.send, basedir, config) if not config.option.nomagic: py.magic.revoke(assertion=1) channel.close() Modified: py/trunk/py/test/rsession/testing/test_executor.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_executor.py (original) +++ py/trunk/py/test/rsession/testing/test_executor.py Sat Feb 10 17:50:47 2007 @@ -11,46 +11,60 @@ if py.std.sys.platform == "win32": py.test.skip("skipping executor tests (some require os.fork)") -class ItemTestPassing(py.test.Item): +class Item(py.test.Item): + def __init__(self, name, config): + super(Item, self).__init__(name) + self._config = config + +class ItemTestPassing(Item): def run(self): return None -class ItemTestFailing(py.test.Item): +class ItemTestFailing(Item): def run(self): assert 0 == 1 -class ItemTestSkipping(py.test.Item): +class ItemTestSkipping(Item): def run(self): py.test.skip("hello") +class ItemTestPrinting(Item): + def run(self): + print "hello" + class TestExecutor(BasicRsessionTest): def test_run_executor(self): - ex = RunExecutor(ItemTestPassing("pass"), config=self.config) + ex = RunExecutor(ItemTestPassing("pass", self.config), config=self.config) outcome = ex.execute() assert outcome.passed - ex = RunExecutor(ItemTestFailing("fail"), config=self.config) + ex = RunExecutor(ItemTestFailing("fail", self.config), config=self.config) outcome = ex.execute() assert not outcome.passed - ex = RunExecutor(ItemTestSkipping("skip"), config=self.config) + ex = RunExecutor(ItemTestSkipping("skip", self.config), config=self.config) outcome = ex.execute() assert outcome.skipped assert not outcome.passed - assert not outcome.excinfo + assert not outcome.excinfo + + def test_run_executor_capture(self): + ex = RunExecutor(ItemTestPrinting("print", self.config), config=self.config) + outcome = ex.execute() + assert outcome.stdout == "hello\n" def test_box_executor(self): - ex = BoxExecutor(ItemTestPassing("pass"), config=self.config) + ex = BoxExecutor(ItemTestPassing("pass", self.config), config=self.config) outcome_repr = ex.execute() outcome = ReprOutcome(outcome_repr) assert outcome.passed - ex = BoxExecutor(ItemTestFailing("fail"), config=self.config) + ex = BoxExecutor(ItemTestFailing("fail", self.config), config=self.config) outcome_repr = ex.execute() outcome = ReprOutcome(outcome_repr) assert not outcome.passed - ex = BoxExecutor(ItemTestSkipping("skip"), config=self.config) + ex = BoxExecutor(ItemTestSkipping("skip", self.config), config=self.config) outcome_repr = ex.execute() outcome = ReprOutcome(outcome_repr) assert outcome.skipped Modified: py/trunk/py/test/rsession/testing/test_master.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_master.py (original) +++ py/trunk/py/test/rsession/testing/test_master.py Sat Feb 10 17:50:47 2007 @@ -137,6 +137,7 @@ return self.config.get_collector_trail(item) def test_slave_setup(self): + py.test.skip("Doesn't work anymore") pkgname = self.pkgpath.basename host = HostInfo("localhost:%s" %(self.tmpdir,)) host.initgateway() Modified: py/trunk/py/test/rsession/testing/test_slave.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_slave.py (original) +++ py/trunk/py/test/rsession/testing/test_slave.py Sat Feb 10 17:50:47 2007 @@ -1,6 +1,6 @@ """ Testing the slave side node code (in a local way). """ -from py.__.test.rsession.slave import SlaveNode, slave_main, setup, PidInfo +from py.__.test.rsession.slave import SlaveNode, slave_main, setup from py.__.test.rsession.outcome import ReprOutcome import py, sys from py.__.test.rsession.testing.basetest import BasicRsessionTest @@ -17,8 +17,7 @@ class TestSlave(BasicRsessionTest): def gettestnode(self): - pidinfo = PidInfo() - node = SlaveNode(self.config, pidinfo, executor=RunExecutor) + node = SlaveNode(self.config, executor=RunExecutor) return node def test_slave_run_passing(self): @@ -74,18 +73,3 @@ node = self.gettestnode() node.run(self.rootcol._getitembynames("py doc log.txt".split()). _get_collector_trail()) - -def test_pidinfo(): - if not hasattr(os, 'fork') or not hasattr(os, 'waitpid'): - py.test.skip("Platform does not support fork") - pidinfo = PidInfo() - pid = os.fork() - if pid: - pidinfo.set_pid(pid) - pidinfo.waitandclear(pid, 0) - else: - import time, sys - time.sleep(.3) - os._exit(0) - # check if this really exits - py.test.raises(OSError, "os.waitpid(pid, 0)") From hpk at codespeak.net Sat Feb 10 18:00:59 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 10 Feb 2007 18:00:59 +0100 (CET) Subject: [py-svn] r38398 - py/trunk/py/test/rsession Message-ID: <20070210170059.AF6211008E@code0.codespeak.net> Author: hpk Date: Sat Feb 10 18:00:58 2007 New Revision: 38398 Modified: py/trunk/py/test/rsession/executor.py Log: for setup/teardown pairs the correct idiom is setup() try: ... finally: teardown() and not to do the setup() within the try, because if that raises, teardown() will likely go wrong as well etc. Modified: py/trunk/py/test/rsession/executor.py ============================================================================== --- py/trunk/py/test/rsession/executor.py (original) +++ py/trunk/py/test/rsession/executor.py Sat Feb 10 18:00:58 2007 @@ -22,8 +22,8 @@ def run(self, capture=True): if capture: + self.item.startcapture() try: - self.item.startcapture() self.item.run() finally: self.item.finishcapture() From hpk at codespeak.net Sat Feb 10 18:09:47 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 10 Feb 2007 18:09:47 +0100 (CET) Subject: [py-svn] r38399 - py/dist/py Message-ID: <20070210170947.6F29A10092@code0.codespeak.net> Author: hpk Date: Sat Feb 10 18:09:45 2007 New Revision: 38399 Removed: py/dist/py/ Log: remove for merge-from-trunk From hpk at codespeak.net Sat Feb 10 18:09:52 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 10 Feb 2007 18:09:52 +0100 (CET) Subject: [py-svn] r38400 - py/dist/py Message-ID: <20070210170952.62BEF100A5@code0.codespeak.net> Author: hpk Date: Sat Feb 10 18:09:50 2007 New Revision: 38400 Added: py/dist/py/ - copied from r38399, py/trunk/py/ Log: use new py-trunk From hpk at codespeak.net Sat Feb 10 21:29:04 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 10 Feb 2007 21:29:04 +0100 (CET) Subject: [py-svn] r38408 - py/trunk/py/apigen Message-ID: <20070210202904.0783E10093@code0.codespeak.net> Author: hpk Date: Sat Feb 10 21:29:03 2007 New Revision: 38408 Added: py/trunk/py/apigen/todo.txt (contents, props changed) Log: my current list of things (not totally sorted in prio order) that i'd like for 0.9 Added: py/trunk/py/apigen/todo.txt ============================================================================== --- (empty file) +++ py/trunk/py/apigen/todo.txt Sat Feb 10 21:29:03 2007 @@ -0,0 +1,50 @@ + +* when listing the methods of a class: + special "__*__" methods should come last except for __init__ + which comes first + +* the page header should read: + + py.path.local API documentation [rev XYZ] + + instead of + + api documentation for path.local + +* have the py/doc/ and apigen page layout have + an api and source link in the menu bar + (e.g.: home doc api source contact getting-started issue) + +* function view: + + def __init__(self, rawcode): + docstring-in-grey-and-not-in-a-box + + and the "show/hide funcinfo" link could be underyling + the full "def __init__(self, rawcode)" or be a link right after + (or maybe before) it. + + goal: consume less vertical space and have the functions + be "sticking" out (the show/hide info link IMO disrupts this + and it's not visually clear it belongs to the function above it) + +* can it be avoided that py.execnet.Channel shows up as a + primary object but still have it documented/linked from + remote_exec()'s "return value"? + +* class attributes are not "properties". can they get their + section? + +* stacktraces: a lot are "duplicates" like: + + /home/hpk/py-trunk/py/test/rsession/hostmanage.py - line 37 + /home/hpk/py-trunk/py/test/rsession/hostmanage.py - line 37 + /home/hpk/py-trunk/py/test/rsession/hostmanage.py - line 37 + + even though they may reference different stackframes, + i think we should by default strip out these duplicates, + this would also reduce the generated html files, right? + +* allow for flexibility regarding linking from + py/doc/*.txt documents to apigen with respect + to where apigen/ docs are located. From fijal at codespeak.net Sat Feb 10 22:56:23 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Sat, 10 Feb 2007 22:56:23 +0100 (CET) Subject: [py-svn] r38422 - py/trunk/py/test/rsession Message-ID: <20070210215623.1353D10093@code0.codespeak.net> Author: fijal Date: Sat Feb 10 22:56:23 2007 New Revision: 38422 Modified: py/trunk/py/test/rsession/local.py Log: Argh. Forgotten to check that in Modified: py/trunk/py/test/rsession/local.py ============================================================================== --- py/trunk/py/test/rsession/local.py (original) +++ py/trunk/py/test/rsession/local.py Sat Feb 10 22:56:23 2007 @@ -22,13 +22,10 @@ raise NotImplementedError() def apigen_runner(item, session, reporter): - startcapture(session) #retval = plain_runner(item, session, reporter) r = ApigenExecutor(item, reporter=reporter, config=session.config) outcome = r.execute(session.tracer) - outcome = ReprOutcome(outcome.make_repr(session.config.option.tbstyle)) - outcome.stdout, outcome.stderr = finishcapture(session) - return outcome + return ReprOutcome(outcome.make_repr(session.config.option.tbstyle)) def exec_runner(item, session, reporter): raise NotImplementedError() From hpk at codespeak.net Sat Feb 10 23:16:48 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sat, 10 Feb 2007 23:16:48 +0100 (CET) Subject: [py-svn] r38427 - py/trunk/py/execnet Message-ID: <20070210221648.1F3C310093@code0.codespeak.net> Author: hpk Date: Sat Feb 10 23:16:47 2007 New Revision: 38427 Modified: py/trunk/py/execnet/gateway.py Log: significantly reducing the trailing "Exception in Thread" messages by having threads die more silently. Most of these threads and tracebacks are anyway on some remote machine, and we need to take care that errors are routed to the place of invocation (e.g. the remote_exec() invoker should get remote exceptions, if the remote thread goes out of scope, the resulting exception will not be seen likely, anyway) Modified: py/trunk/py/execnet/gateway.py ============================================================================== --- py/trunk/py/execnet/gateway.py (original) +++ py/trunk/py/execnet/gateway.py Sat Feb 10 23:16:47 2007 @@ -104,7 +104,7 @@ self._trace("received <- %r" % msg) msg.received(self) except sysex: - raise + break except EOFError: break except: @@ -131,7 +131,7 @@ self._traceex(excinfo) if msg is not None: msg.post_sent(self, excinfo) - raise + break else: self._trace('sent -> %r' % msg) msg.post_sent(self) @@ -167,7 +167,7 @@ close() self._trace("execution finished:", repr(source)[:50]) except (KeyboardInterrupt, SystemExit): - raise + pass except: excinfo = exc_info() l = traceback.format_exception(*excinfo) From guido at codespeak.net Sun Feb 11 02:54:23 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Sun, 11 Feb 2007 02:54:23 +0100 (CET) Subject: [py-svn] r38438 - py/trunk/py/test/rsession Message-ID: <20070211015423.A339810093@code0.codespeak.net> Author: guido Date: Sun Feb 11 02:54:22 2007 New Revision: 38438 Modified: py/trunk/py/test/rsession/local.py Log: Rolling back r38422 and part of r38397 because it made that a lot of stuff was printed to stdout/err. Modified: py/trunk/py/test/rsession/local.py ============================================================================== --- py/trunk/py/test/rsession/local.py (original) +++ py/trunk/py/test/rsession/local.py Sun Feb 11 02:54:22 2007 @@ -8,6 +8,18 @@ from py.__.test.rsession import repevent from py.__.test.rsession.outcome import ReprOutcome +# XXX copied from session.py +def startcapture(session): + if not session.config.option.nocapture: + session._capture = py.io.StdCapture() + +def finishcapture(session): + if hasattr(session, '_capture'): + capture = session._capture + del session._capture + return capture.reset() + return "", "" + def box_runner(item, session, reporter): r = BoxExecutor(item, config=session.config) return ReprOutcome(r.execute()) @@ -23,9 +35,12 @@ def apigen_runner(item, session, reporter): #retval = plain_runner(item, session, reporter) + startcapture(session) r = ApigenExecutor(item, reporter=reporter, config=session.config) outcome = r.execute(session.tracer) - return ReprOutcome(outcome.make_repr(session.config.option.tbstyle)) + outcome = ReprOutcome(outcome.make_repr(session.config.option.tbstyle)) + outcome.stdout, outcome.stderr = finishcapture(session) + return outcome def exec_runner(item, session, reporter): raise NotImplementedError() From guido at codespeak.net Sun Feb 11 03:04:40 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Sun, 11 Feb 2007 03:04:40 +0100 (CET) Subject: [py-svn] r38439 - in py/trunk/py: apigen apigen/testing doc Message-ID: <20070211020440.547371008E@code0.codespeak.net> Author: guido Date: Sun Feb 11 03:04:36 2007 New Revision: 38439 Modified: py/trunk/py/apigen/apigen.py py/trunk/py/apigen/html.py py/trunk/py/apigen/htmlgen.py py/trunk/py/apigen/layout.py py/trunk/py/apigen/style.css py/trunk/py/apigen/testing/test_apigen_example.py py/trunk/py/apigen/todo.txt py/trunk/py/doc/confrest.py Log: Fixed a list of things suggested by hpk: changed method order in class pages, changed page titles, added links to the api and source index from the nav bar (also in py/doc html), changed function views, made it possible to remove an item from the navigation, changed header 'properties' to 'class attributes and properties', removed duplicate stack traces (in a somewhat unsatisfying way, needs revisiting later I think). Modified: py/trunk/py/apigen/apigen.py ============================================================================== --- py/trunk/py/apigen/apigen.py (original) +++ py/trunk/py/apigen/apigen.py Sun Feb 11 03:04:36 2007 @@ -30,6 +30,7 @@ pkgname, pkgdict = get_documentable_items_pkgdir(pkgdir) from py.__.execnet.channel import Channel pkgdict['execnet.Channel'] = Channel + Channel.__apigen_hide_from_nav__ = True return pkgname, pkgdict def build(pkgdir, dsa, capture): Modified: py/trunk/py/apigen/html.py ============================================================================== --- py/trunk/py/apigen/html.py (original) +++ py/trunk/py/apigen/html.py Sun Feb 11 03:04:36 2007 @@ -25,7 +25,7 @@ class ClassDef(html.div): def __init__(self, classname, bases, docstring, sourcelink, - properties, methods): + attrs, methods): header = H.h1('class %s(' % (classname,)) for i, (name, href) in py.builtin.enumerate(bases): if i > 0: @@ -40,9 +40,9 @@ '*no docstring available*'), sourcelink, class_='classdoc')) - if properties: - self.append(H.h2('properties:')) - for name, val in properties: + if attrs: + self.append(H.h2('class attributes and properties:')) + for name, val in attrs: self.append(H.PropertyDescription(name, val)) if methods: self.append(H.h2('methods:')) @@ -58,20 +58,32 @@ class FunctionDescription(Description): def __init__(self, localname, argdesc, docstring, valuedesc, csource, callstack): - fd = H.FunctionDef(localname, argdesc) - ds = H.Docstring(docstring or '*no docstring available*') - fi = H.FunctionInfo(valuedesc, csource, callstack) + infoid = 'info_%s' % (localname.replace('.', '_dot_'),) + docstringid = 'docstring_%s' % (localname.replace('.', '_dot_'),) + fd = H.FunctionDef(localname, argdesc, + onclick=('showhideel(' + 'document.getElementById("%s")); ' + 'showhideel(' + 'document.getElementById("%s")); ' + 'this.scrollIntoView()' % ( + infoid, docstringid))) + ds = H.Docstring(docstring or '*no docstring available*', + id=docstringid) + fi = H.FunctionInfo(valuedesc, csource, callstack, + id=infoid, style="display: none") super(H.FunctionDescription, self).__init__(fd, ds, fi) class FunctionDef(html.h2): - def __init__(self, name, argdesc): - super(H.FunctionDef, self).__init__('def %s%s:' % (name, argdesc)) + style = html.Style(cursor='pointer', color='blue') + def __init__(self, name, argdesc, **kwargs): + super(H.FunctionDef, self).__init__('def %s%s:' % (name, argdesc), + **kwargs) class FunctionInfo(html.div): - def __init__(self, valuedesc, csource, callstack): - super(H.FunctionInfo, self).__init__( - H.Hideable('funcinfo', 'funcinfo', valuedesc, H.br(), csource, - callstack)) + def __init__(self, valuedesc, csource, callstack, **kwargs): + super(H.FunctionInfo, self).__init__(valuedesc, H.br(), csource, + callstack, class_='funcinfo', + **kwargs) class PropertyDescription(html.div): def __init__(self, name, value): @@ -86,8 +98,9 @@ class ParameterDescription(html.div): pass - class Docstring(html.pre): - pass + class Docstring(html.div): + style = html.Style(white_space='pre', color='#666', + margin_left='1em', margin_bottom='1em') class Navigation(html.div): #style = html.Style(min_height='99%', float='left', margin_top='1.2em', Modified: py/trunk/py/apigen/htmlgen.py ============================================================================== --- py/trunk/py/apigen/htmlgen.py (original) +++ py/trunk/py/apigen/htmlgen.py Sun Feb 11 03:04:36 2007 @@ -14,6 +14,8 @@ html = py.xml.html raw = py.xml.raw +REDUCE_CALLSITES = True + def is_navigateable(name): return (not is_private(name) and name != '__doc__') @@ -24,7 +26,7 @@ # XXX do we need to skip more manually here? if (name not in dir(object) and name not in ['__doc__', '__dict__', '__name__', '__module__', - '__weakref__']): + '__weakref__', '__apigen_hide_from_nav__']): return True return False @@ -136,10 +138,15 @@ break return snippet +_get_obj_cache = {} def get_obj(dsa, pkg, dotted_name): full_dotted_name = '%s.%s' % (pkg.__name__, dotted_name) if dotted_name == '': return pkg + try: + return _get_obj_cache[dotted_name] + except KeyError: + pass path = dotted_name.split('.') ret = pkg for item in path: @@ -147,10 +154,13 @@ ret = getattr(ret, item, marker) if ret is marker: try: - return dsa.get_obj(dotted_name) + ret = dsa.get_obj(dotted_name) except KeyError: raise NameError('can not access %s in %s' % (item, full_dotted_name)) + else: + break + _get_obj_cache[dotted_name] = ret return ret def get_rel_sourcepath(projpath, filename, default=None): @@ -419,6 +429,10 @@ def build_methods(self, dotted_name): ret = [] methods = self.dsa.get_class_methods(dotted_name) + # move all __*__ methods to the back + methods = ([m for m in methods if not m.startswith('_')] + + [m for m in methods if m.startswith('_')]) + # except for __init__, which should be first if '__init__' in methods: methods.remove('__init__') methods.insert(0, '__init__') @@ -437,7 +451,8 @@ ) for dotted_name in sorted(item_dotted_names): itemname = dotted_name.split('.')[-1] - if not is_navigateable(itemname): + if (not is_navigateable(itemname) or + self.is_hidden_from_nav(dotted_name)): continue snippet.append( H.NamespaceItem( @@ -463,7 +478,10 @@ nav = self.build_navigation(dotted_name, False) reltargetpath = "api/%s.html" % (dotted_name,) self.linker.set_link(dotted_name, reltargetpath) - title = 'api documentation for %s' % (dotted_name,) + title = '%s API documentation' % (dotted_name,) + rev = self.get_revision(dotted_name) + if rev: + title += ' [rev. %s]' % (rev,) self.write_page(title, reltargetpath, tag, nav) return passed @@ -479,7 +497,10 @@ nav = self.build_navigation(dotted_name, False) reltargetpath = "api/%s.html" % (dotted_name,) self.linker.set_link(dotted_name, reltargetpath) - title = 'api documentation for %s' % (dotted_name,) + title = '%s API documentation' % (dotted_name,) + rev = self.get_revision(dotted_name) + if rev: + title += ' [rev. %s]' % (rev,) self.write_page(title, reltargetpath, tag, nav) return passed @@ -528,6 +549,8 @@ sibname = sibpath[-1] if not is_navigateable(sibname): continue + if self.is_hidden_from_nav(dn): + continue navitems.append(H.NavigationItem(self.linker, dn, sibname, depth, selected)) if selected: @@ -595,10 +618,18 @@ def is_in_pkg(self, sourcefile): return py.path.local(sourcefile).relto(self.projpath) + _processed_callsites = {} def build_callsites(self, dotted_name): callstack = self.dsa.get_function_callpoints(dotted_name) cslinks = [] for i, (cs, _) in enumerate(callstack): + if REDUCE_CALLSITES: + key = (cs[0].filename, cs[0].lineno) + if key in self._processed_callsites: + # process one call site per line of test code when + # REDUCE_CALLSITES is set to True + continue + self._processed_callsites[key] = 1 link = self.build_callsite(dotted_name, cs, i) cslinks.append(link) return cslinks @@ -660,3 +691,22 @@ tbtag.append(H.div(*colored)) return tbtag + def is_hidden_from_nav(self, dotted_name): + obj = get_obj(self.dsa, self.pkg, dotted_name) + return getattr(obj, '__apigen_hide_from_nav__', False) + + def get_revision(self, dotted_name): + obj = get_obj(self.dsa, self.pkg, dotted_name) + try: + sourcefile = inspect.getsourcefile(obj) + except TypeError: + return None + if sourcefile is None: + return None + if sourcefile[-1] in ['o', 'c']: + sourcefile = sourcefile[:-1] + wc = py.path.svnwc(sourcefile) + if wc.check(versioned=True): + return wc.status().rev + return None + Modified: py/trunk/py/apigen/layout.py ============================================================================== --- py/trunk/py/apigen/layout.py (original) +++ py/trunk/py/apigen/layout.py Sun Feb 11 03:04:36 2007 @@ -20,6 +20,7 @@ self.nav = kwargs.pop('nav') self.relpath = kwargs.pop('relpath') super(LayoutPage, self).__init__(*args, **kwargs) + self.project.logo.attr.id = 'logo' def set_content(self, contentel): self.contentspace.append(contentel) Modified: py/trunk/py/apigen/style.css ============================================================================== --- py/trunk/py/apigen/style.css (original) +++ py/trunk/py/apigen/style.css Sun Feb 11 03:04:36 2007 @@ -2,6 +2,11 @@ font-size: 0.8em; } +#logo { + position: relative; + position: fixed; +} + div.sidebar { font-family: Verdana, Helvetica, Arial, sans-serif; font-size: 0.9em; @@ -9,6 +14,7 @@ vertical-align: top; margin-top: 0.5em; position: absolute; + position: fixed; top: 130px; left: 4px; bottom: 4px; @@ -34,6 +40,10 @@ list-style-type: none; } +h2 { + padding-top: 0.5em; +} + .code a { color: blue; font-weight: bold; @@ -42,6 +52,7 @@ .lineno { line-height: 1.4em; + height: 1.4em; text-align: right; color: #555; width: 3em; @@ -52,6 +63,7 @@ .code { line-height: 1.4em; + height: 1.4em; padding-left: 1em; white-space: pre; font-family: monospace, Monaco; Modified: py/trunk/py/apigen/testing/test_apigen_example.py ============================================================================== --- py/trunk/py/apigen/testing/test_apigen_example.py (original) +++ py/trunk/py/apigen/testing/test_apigen_example.py Sun Feb 11 03:04:36 2007 @@ -37,6 +37,9 @@ " get_somevar docstring " return self.somevar SomeInstance = SomeClass(10) + class SomeHiddenClass(object): + " docstring somehiddenclass " + __apigen_hide_from_nav__ = True # hide it from the navigation """)) temp.ensure('pkg/somesubclass.py').write(py.code.Source("""\ from someclass import SomeClass @@ -59,6 +62,7 @@ 'main.SomeInstance': ('./someclass.py', 'SomeInstance'), 'main.SomeSubClass': ('./somesubclass.py', 'SomeSubClass'), 'main.SomeSubClass': ('./somesubclass.py', 'SomeSubClass'), + 'main.SomeHiddenClass': ('./someclass.py', 'SomeHiddenClass'), 'other': ('./somenamespace.py', '*'), '_test': ('./somenamespace.py', '*'), }) @@ -105,8 +109,9 @@ 'main.SomeClass', 'main.SomeSubClass', 'main.SomeInstance', + 'main.SomeHiddenClass', 'other.foo', - 'other.bar', + 'other.baz', '_test']) self.namespace_tree = namespace_tree self.apb = ApiPageBuilder(base, linker, self.dsa, @@ -284,7 +289,8 @@ self.apb.build_function_pages(['main.sub.func']) self.apb.build_class_pages(['main.SomeClass', 'main.SomeSubClass', - 'main.SomeInstance']) + 'main.SomeInstance', + 'main.SomeHiddenClass']) self.linker.replace_dirpath(self.base, False) html = self.base.join('api/main.sub.func.html').read() print html Modified: py/trunk/py/apigen/todo.txt ============================================================================== --- py/trunk/py/apigen/todo.txt (original) +++ py/trunk/py/apigen/todo.txt Sun Feb 11 03:04:36 2007 @@ -3,6 +3,8 @@ special "__*__" methods should come last except for __init__ which comes first + DONE + * the page header should read: py.path.local API documentation [rev XYZ] @@ -11,10 +13,15 @@ api documentation for path.local + DONE, title changed and if possible (read: if source file in SVN) rev is + retrieved and added + * have the py/doc/ and apigen page layout have an api and source link in the menu bar (e.g.: home doc api source contact getting-started issue) + DONE + * function view: def __init__(self, rawcode): @@ -28,13 +35,21 @@ be "sticking" out (the show/hide info link IMO disrupts this and it's not visually clear it belongs to the function above it) + DONE, but please review if you like it like this... + * can it be avoided that py.execnet.Channel shows up as a primary object but still have it documented/linked from remote_exec()'s "return value"? + DONE: if you set an attribute __hide_from_nav__ to True on an + object somehow, it is hidden from the navigation + * class attributes are not "properties". can they get their section? + DONE: renamed title to 'class attributes and properties' + (as discussed) + * stacktraces: a lot are "duplicates" like: /home/hpk/py-trunk/py/test/rsession/hostmanage.py - line 37 @@ -45,6 +60,12 @@ i think we should by default strip out these duplicates, this would also reduce the generated html files, right? + DONE, although I'm not happy with it... I'd rather only display call sites + from calls in the test somehow or something... + * allow for flexibility regarding linking from py/doc/*.txt documents to apigen with respect to where apigen/ docs are located. + + LATER, as discussed + Modified: py/trunk/py/doc/confrest.py ============================================================================== --- py/trunk/py/doc/confrest.py (original) +++ py/trunk/py/doc/confrest.py Sun Feb 11 03:04:36 2007 @@ -33,6 +33,10 @@ self.menubar = html.div( html.a("home", href="home.html", class_="menu"), " ", html.a("doc", href="index.html", class_="menu"), " ", + html.a("api", href="../../apigen/api/index.html", class_="menu"), + " ", + html.a("source", href="../../apigen/source/index.html", + class_="menu"), " ", html.a("contact", href="contact.html", class_="menu"), " ", html.a("getting-started", href="getting-started.html", class_="menu"), " ", id="menubar", From guido at codespeak.net Sun Feb 11 03:21:46 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Sun, 11 Feb 2007 03:21:46 +0100 (CET) Subject: [py-svn] r38440 - py/trunk/py/apigen Message-ID: <20070211022146.CB9DF10093@code0.codespeak.net> Author: guido Date: Sun Feb 11 03:21:45 2007 New Revision: 38440 Modified: py/trunk/py/apigen/html.py py/trunk/py/apigen/style.css Log: Made that the blue links are colored from the stylesheet. Modified: py/trunk/py/apigen/html.py ============================================================================== --- py/trunk/py/apigen/html.py (original) +++ py/trunk/py/apigen/html.py Sun Feb 11 03:21:45 2007 @@ -74,10 +74,11 @@ super(H.FunctionDescription, self).__init__(fd, ds, fi) class FunctionDef(html.h2): - style = html.Style(cursor='pointer', color='blue') + style = html.Style(cursor='pointer') def __init__(self, name, argdesc, **kwargs): + class_ = kwargs.pop('class_', 'funcdef') super(H.FunctionDef, self).__init__('def %s%s:' % (name, argdesc), - **kwargs) + class_=class_, **kwargs) class FunctionInfo(html.div): def __init__(self, valuedesc, csource, callstack, **kwargs): Modified: py/trunk/py/apigen/style.css ============================================================================== --- py/trunk/py/apigen/style.css (original) +++ py/trunk/py/apigen/style.css Sun Feb 11 03:21:45 2007 @@ -44,6 +44,10 @@ padding-top: 0.5em; } +h2.funcdef { + color: blue; +} + .code a { color: blue; font-weight: bold; From hpk at codespeak.net Sun Feb 11 09:32:28 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 11 Feb 2007 09:32:28 +0100 (CET) Subject: [py-svn] r38441 - py/trunk/py/execnet/testing Message-ID: <20070211083228.16FA210094@code0.codespeak.net> Author: hpk Date: Sun Feb 11 09:32:27 2007 New Revision: 38441 Modified: py/trunk/py/execnet/testing/test_gateway.py Log: increase hard-coded timeouts a bit (waitclose() will not neccessarily wait that long, it returns as soon as the channel is closed so it's not as bad as outright sleeping) Modified: py/trunk/py/execnet/testing/test_gateway.py ============================================================================== --- py/trunk/py/execnet/testing/test_gateway.py (original) +++ py/trunk/py/execnet/testing/test_gateway.py Sun Feb 11 09:32:27 2007 @@ -98,11 +98,11 @@ def test_remote_exec_waitclose(self): channel = self.gw.remote_exec('pass') - channel.waitclose(timeout=1.0) + channel.waitclose(timeout=5.0) def test_remote_exec_waitclose_2(self): channel = self.gw.remote_exec('def gccycle(): pass') - channel.waitclose(timeout=1.0) + channel.waitclose(timeout=5.0) def test_remote_exec_waitclose_noarg(self): channel = self.gw.remote_exec('pass') @@ -110,7 +110,7 @@ def test_remote_exec_error_after_close(self): channel = self.gw.remote_exec('pass') - channel.waitclose(timeout=1.0) + channel.waitclose(timeout=5.0) py.test.raises(IOError, channel.send, 0) def test_remote_exec_channel_anonymous(self): From hpk at codespeak.net Sun Feb 11 09:44:16 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 11 Feb 2007 09:44:16 +0100 (CET) Subject: [py-svn] r38442 - py/dist/py/doc Message-ID: <20070211084416.1289010093@code0.codespeak.net> Author: hpk Date: Sun Feb 11 09:44:15 2007 New Revision: 38442 Modified: py/dist/py/doc/releasescheme.txt Log: fixing and simplifying the release scheme doc Modified: py/dist/py/doc/releasescheme.txt ============================================================================== --- py/dist/py/doc/releasescheme.txt (original) +++ py/dist/py/doc/releasescheme.txt Sun Feb 11 09:44:15 2007 @@ -13,35 +13,30 @@ Scenario "no svn and just let me play, please" ============================================== -If you don't have a subversion client you can download -specific versions by going to +you may download py lib versions by going to: http://codespeak.net/download/py -and pick a suitable archive file. You need to -unpack it and may want to run `setup.py` to install -it in a system-wide manner. +and picking a suitable archive file. You need to +unpack it and run `setup.py` to install it in +a system-wide manner. Installation Scenario "svn + stay close to released versions" ------------------------------------------------------------- -If you have a subversion client (you can easily install -one, look at PyPy's `svn help document`_) it is recommended that you choose the following -method to install, irrespective if you want to install -things via distutils. Start by issueing the following -shell command (or a graphical equivalent):: +If you want to follow stable snapshots of the py lib, +then you may use svn to checkout and update such +stable "dist" snapshosts:: svn co http://codespeak.net/svn/py/dist py-dist -Now we want to make sure that the `py-dist/py/bin` directory -gets on your shell search path in order to have "py.test" -directly available. You basically have two choices: - -1) include in your local shell environment startup the equivalent - of ``eval `python .../py-dist/py/env.py```. - XXX describe this in more detail (rip out from `getting started`_). +You may or may not use this checkout globally: -2) go to the `py-dist` directory and run `python setup.py install`. +1) go to the `py-dist` directory and run `python setup.py install` + after each update. + +2) add the localhost of the checked out `py-dist/py/bin` to your + `PATH` shell variable. .. _`getting started`: getting-started.html .. _`svn help document`: http://codespeak.net/pypy/index.cgi?doc/getting_started.html#subversion @@ -57,13 +52,13 @@ or to a specific version via:: - svn switch http://codespeak.net/svn/py/tag/py-X.Y.Z + svn switch http://codespeak.net/svn/py/release/py-X.Y.Z or to a specific release branch:: svn switch http://codespeak.net/svn/py/branch/py-X.Y -If you choose the option No. 2) above you have to repeat +If you choose the option No. 1) above you have to repeat the distutils install after each checkout/switch. .. _`svn-external scenario`: @@ -72,16 +67,16 @@ Installation Scenario "svn + include py lib as an external" =========================================================== -OK, so you want to have the py lib supporting your -application and are using subversion? Great because -things are quite easy and flexible for this scenario. +If you want to use the py lib as a support library +for your (svn controlled) project, you may add it +as an "svn external". -Tying the py lib into your subversion controled project +Putting the py lib into your subversion controled project ------------------------------------------------------- On the `DIRECTORY` which contains your root package issue:: - svn pe 'svn:externals' DIRECTORY + svn propedit 'svn:externals' DIRECTORY and add the following line to your (possibly empty) list of svn-externals:: @@ -89,25 +84,8 @@ py http://codespeak.net/svn/py/dist This will make your projcet follow the most recent -release of the py lib. (please substitute `dist` for `trunk` -if you want to follow py lib development, this will let -you catch interaction problems early on ...). -If you now issue an `svn up` on your `DIRECTORY` you -will retrieve the external into your application. - -If you want to follow a minor release branch seamlessly -then use the following line:: - - py http://codespeak.net/svn/py/branch/py-X.Y - -where `X.Y` indicate the branch you want to follow. +stable snapshot of the py lib. (please substitute `dist` +for `trunk` if you want to follow py lib development). -If you want to use a very fixed version of the py lib -you can tie to a specific release:: - - py http://codespeak.net/svn/py/tag/py-X.Y.Z - -Integrating the py lib into your distribution ----------------------------------------------- - -XXX +If you now issue an `svn up` on your `DIRECTORY` you +will retrieve the py lib into your package root directory. From hpk at codespeak.net Sun Feb 11 12:01:28 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 11 Feb 2007 12:01:28 +0100 (CET) Subject: [py-svn] r38447 - py/trunk/py/path/svn Message-ID: <20070211110128.28B0410097@code0.codespeak.net> Author: hpk Date: Sun Feb 11 12:01:27 2007 New Revision: 38447 Modified: py/trunk/py/path/svn/wccommand.py Log: privatize internal regular expressions Modified: py/trunk/py/path/svn/wccommand.py ============================================================================== --- py/trunk/py/path/svn/wccommand.py (original) +++ py/trunk/py/path/svn/wccommand.py Sun Feb 11 12:01:27 2007 @@ -193,7 +193,7 @@ """ rename this path to target. """ py.process.cmdexec("svn move --force %s %s" %(str(self), str(target))) - rex_status = re.compile(r'\s+(\d+|-)\s+(\S+)\s+(\S+)\s+(.*)') + _rex_status = re.compile(r'\s+(\d+|-)\s+(\S+)\s+(\S+)\s+(.*)') def status(self, updates=0, rec=0, externals=0): """ return (collective) Status object for this file. """ @@ -251,7 +251,7 @@ #elif c0 in '~!' or c4 == 'S': # raise NotImplementedError("received flag %r" % c0) - m = self.rex_status.match(rest) + m = self._rex_status.match(rest) if not m: if c7 == '*': fn = rest.strip() @@ -328,7 +328,7 @@ result.append((int(rev), name, line)) return result - rex_commit = re.compile(r'.*Committed revision (\d+)\.$', re.DOTALL) + _rex_commit = re.compile(r'.*Committed revision (\d+)\.$', re.DOTALL) def commit(self, message=""): """commit() returns None if there was nothing to commit and the revision number of the commit otherwise. @@ -339,7 +339,7 @@ except KeyError: pass if out: - m = self.rex_commit.match(out) + m = self._rex_commit.match(out) return int(m.group(1)) def propset(self, name, value, *args): From hpk at codespeak.net Sun Feb 11 12:05:52 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 11 Feb 2007 12:05:52 +0100 (CET) Subject: [py-svn] r38448 - py/trunk/py/apigen Message-ID: <20070211110552.E94ED10097@code0.codespeak.net> Author: hpk Date: Sun Feb 11 12:05:52 2007 New Revision: 38448 Modified: py/trunk/py/apigen/htmlgen.py Log: shorter title Modified: py/trunk/py/apigen/htmlgen.py ============================================================================== --- py/trunk/py/apigen/htmlgen.py (original) +++ py/trunk/py/apigen/htmlgen.py Sun Feb 11 12:05:52 2007 @@ -478,7 +478,7 @@ nav = self.build_navigation(dotted_name, False) reltargetpath = "api/%s.html" % (dotted_name,) self.linker.set_link(dotted_name, reltargetpath) - title = '%s API documentation' % (dotted_name,) + title = '%s API' % (dotted_name,) rev = self.get_revision(dotted_name) if rev: title += ' [rev. %s]' % (rev,) @@ -497,7 +497,7 @@ nav = self.build_navigation(dotted_name, False) reltargetpath = "api/%s.html" % (dotted_name,) self.linker.set_link(dotted_name, reltargetpath) - title = '%s API documentation' % (dotted_name,) + title = '%s API' % (dotted_name,) rev = self.get_revision(dotted_name) if rev: title += ' [rev. %s]' % (rev,) From hpk at codespeak.net Sun Feb 11 12:14:44 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 11 Feb 2007 12:14:44 +0100 (CET) Subject: [py-svn] r38449 - py/trunk/py/apigen Message-ID: <20070211111444.BAB5E1008D@code0.codespeak.net> Author: hpk Date: Sun Feb 11 12:14:44 2007 New Revision: 38449 Modified: py/trunk/py/apigen/todo.txt Log: removed DONE issues, added one more (sources page) and comments. Modified: py/trunk/py/apigen/todo.txt ============================================================================== --- py/trunk/py/apigen/todo.txt (original) +++ py/trunk/py/apigen/todo.txt Sun Feb 11 12:14:44 2007 @@ -1,26 +1,7 @@ -* when listing the methods of a class: - special "__*__" methods should come last except for __init__ - which comes first +* source page headers should read: - DONE - -* the page header should read: - - py.path.local API documentation [rev XYZ] - - instead of - - api documentation for path.local - - DONE, title changed and if possible (read: if source file in SVN) rev is - retrieved and added - -* have the py/doc/ and apigen page layout have - an api and source link in the menu bar - (e.g.: home doc api source contact getting-started issue) - - DONE + py/apigen sources [rev XXX] * function view: @@ -36,32 +17,9 @@ and it's not visually clear it belongs to the function above it) DONE, but please review if you like it like this... - -* can it be avoided that py.execnet.Channel shows up as a - primary object but still have it documented/linked from - remote_exec()'s "return value"? - - DONE: if you set an attribute __hide_from_nav__ to True on an - object somehow, it is hidden from the navigation - -* class attributes are not "properties". can they get their - section? - - DONE: renamed title to 'class attributes and properties' - (as discussed) - -* stacktraces: a lot are "duplicates" like: - - /home/hpk/py-trunk/py/test/rsession/hostmanage.py - line 37 - /home/hpk/py-trunk/py/test/rsession/hostmanage.py - line 37 - /home/hpk/py-trunk/py/test/rsession/hostmanage.py - line 37 - - even though they may reference different stackframes, - i think we should by default strip out these duplicates, - this would also reduce the generated html files, right? - - DONE, although I'm not happy with it... I'd rather only display call sites - from calls in the test somehow or something... + + XXX it's nice but can you keep the docstring visible when + more information is displayed/toggled? * allow for flexibility regarding linking from py/doc/*.txt documents to apigen with respect From hpk at codespeak.net Sun Feb 11 12:52:45 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 11 Feb 2007 12:52:45 +0100 (CET) Subject: [py-svn] r38451 - py/trunk/py/doc Message-ID: <20070211115245.2FE4810089@code0.codespeak.net> Author: hpk Date: Sun Feb 11 12:52:44 2007 New Revision: 38451 Added: py/trunk/py/doc/release-0.9.0.txt (contents, props changed) Log: a draft release announcement Added: py/trunk/py/doc/release-0.9.0.txt ============================================================================== --- (empty file) +++ py/trunk/py/doc/release-0.9.0.txt Sun Feb 11 12:52:44 2007 @@ -0,0 +1,30 @@ +py lib 0.9.0: py.test, distributed execution and dev support +====================================================================== + +Welcome to the 0.9.0 py lib (including py.test) release! +Main API/Tool Features: + +* py.test: cross-project testing tool with many advanced features +* py.execnet: ad-hoc code distribution to SSH, Socket and local sub processes +* py.magic.greenlet: micro-threads on standard CPython ("stackless-light") +* py.path: path abstractions over local and subversion files +* rich documentation of py's exported API +* tested against Win32, Linux and OSX + +All these features and their API have extensive documentation, +generated with "apigen", which we intend to make accessible +for other python projects as well. + +Here is the entry point for installing the py lib: + + http://codespeak.net/py/XXX # figure out exact scheme + +and here is the main entry point into the documentation: + + http://codespeak.net/py/XXX # figure out exact scheme + +best and have fun and let us know what you think! + +holger krekel, Maciej Fijalkowski, +Guido Wesdorp, Carl Friedrich Bolz + From hpk at codespeak.net Sun Feb 11 12:53:43 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 11 Feb 2007 12:53:43 +0100 (CET) Subject: [py-svn] r38452 - py/trunk/py Message-ID: <20070211115343.3F35510093@code0.codespeak.net> Author: hpk Date: Sun Feb 11 12:53:42 2007 New Revision: 38452 Modified: py/trunk/py/__init__.py Log: bumping version number to 0.9.0-beta Modified: py/trunk/py/__init__.py ============================================================================== --- py/trunk/py/__init__.py (original) +++ py/trunk/py/__init__.py Sun Feb 11 12:53:42 2007 @@ -5,7 +5,7 @@ """ from initpkg import initpkg -version = "0.8.80-alpha2" +version = "0.9.0-beta" initpkg(__name__, description = "py.test and the py lib", @@ -16,10 +16,11 @@ download_url = "http://codespeak.net/download/py/%s.tar.gz" %(version,), license = "MIT license", platforms = ['unix', 'linux', 'cygwin'], - author = "holger krekel, Armin Rigo, Guido Wesdorp, Maciej Fijalkowski & others", + author = "holger krekel, Carl Friedrich Bolz, Guido Wesdorp, Maciej Fijalkowski, Armin Rigo & others", author_email = "py-dev at codespeak.net", long_description = globals()['__doc__'], + # EXPORTED API exportdefs = { # helpers for use from test functions or collectors 'test.__doc__' : ('./test/__init__.py', '__doc__'), From hpk at codespeak.net Sun Feb 11 14:26:05 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 11 Feb 2007 14:26:05 +0100 (CET) Subject: [py-svn] r38455 - in py/trunk/py/doc: . future Message-ID: <20070211132605.EF7791009C@code0.codespeak.net> Author: hpk Date: Sun Feb 11 14:26:03 2007 New Revision: 38455 Added: py/trunk/py/doc/download.txt - copied, changed from r38451, py/trunk/py/doc/getting-started.txt Removed: py/trunk/py/doc/api.txt py/trunk/py/doc/getting-started.txt py/trunk/py/doc/releasescheme.txt Modified: py/trunk/py/doc/TODO.txt py/trunk/py/doc/contact.txt py/trunk/py/doc/execnet.txt py/trunk/py/doc/future/future.txt py/trunk/py/doc/index.txt py/trunk/py/doc/test.txt py/trunk/py/doc/why_py.txt Log: various merging of startup documentation and rewriting/refactoring information and references. clarifying release-structure a bit. Modified: py/trunk/py/doc/TODO.txt ============================================================================== --- py/trunk/py/doc/TODO.txt (original) +++ py/trunk/py/doc/TODO.txt Sun Feb 11 14:26:03 2007 @@ -59,9 +59,8 @@ * (DONE/c-modules don't) see if things work on Win32 (partially done) -* refine and implement `releasescheme`_ +* (partly DONE) refine and implement releasescheme/download -.. _releasescheme: releasescheme.html APIGEN / source viewer ------------------------------------- Deleted: /py/trunk/py/doc/api.txt ============================================================================== --- /py/trunk/py/doc/api.txt Sun Feb 11 14:26:03 2007 +++ (empty file) @@ -1,19 +0,0 @@ -The py lib API -============== - -This is a placeholder for upcoming API documentation. We -intend to mostly generate this API description from the py -lib's entry points, their docstrings and cross-reference it -with its tests and examples that are already available -at our little `getting started`_ chapter. - -Your tests are your API insurance -================================= - -Tests are the primary assurance of API compatilibity. If you -would like to ensure some behaviour gets preserved across major -releases you have to write a test. Note though, that the -documentation may state additional restrictions which -take precedence. - -.. _`getting started`: getting-started.html Modified: py/trunk/py/doc/contact.txt ============================================================================== --- py/trunk/py/doc/contact.txt (original) +++ py/trunk/py/doc/contact.txt Sun Feb 11 14:26:03 2007 @@ -1,15 +1,81 @@ py lib contact and communication =================================== -* `development mailing list`_ for conceptual and coding discussions (low to medium traffic). +.. contents:: +.. sectnum:: -* `subversion commit mailing list`_ all updates to the trunk/branch source and documentation tree. +IRC Channel #pylib on irc.freenode.net +-------------------------------------------- -* `development bug/feature tracker`_ for filing bugs and feature requests. +The #pylib channel on freenode displays all commits to the py lib +and you are welcome to lurk or to ask questions there! -* IRC Channel #pylib on irc.freenode.net +`py-dev`_ developers mailing list +----------------------------------- -.. _`subversion commit mailing list`: http://codespeak.net/mailman/listinfo/py-svn -.. _`development mailing list`: http://codespeak.net/mailman/listinfo/py-dev +If you see bugs and/or can provide patches, please +subscribe to the `py-dev developers list`_. +As of Febrary 2007 it has medium to low traffic. + + +`py-svn`_ commit mailing list +----------------------------------- + +If you'd like to see ongoing development commits, +please subscribe to: + + `py-svn general commit mailing list`_ + +This list (as of February 2007) has medium to high traffic. + + +`development bug/feature tracker`_ +--------------------------------------------- + +This (somewhat old) roundup instance still serves +to file bugs and track issues. However, we also +keep a list of "TODOs" in various directories. + + +Coding and communication +------------------------ + +We are practicing what could be called documentation, +vision, discussion and automated-test driven development. +In the `future`_ book we try to layout visions and ideas for +the near coding feature to give a means for preliminary +feedback before code hits the ground. + +With our `coding style`_ we are mostly following +cpython guidance with some additional restrictions +some of which projects like twisted_ or zope3_ have +adopted in similar ways. + +.. _`zope3`: http://zope3.zwiki.org/ +.. _twisted: http://www.twistedmatrix.org +.. _future: future/future.html + +.. _`get an account`: + + +get an account on codespeak +--------------------------- + +codespeak_ is employing a liberal committing scheme. If you know +someone who is active on codespeak already or you are otherwise known in +the community then you will most probably just get access. But even if +you are new to the python developer community you may still get one if +you want to improve things and can be expected to honour the +style of coding and communication. + +.. _`coding style`: coding-style.html +.. _us: http://codespeak.net/mailman/listinfo/py-dev +.. _codespeak: http://codespeak.net/ +.. _`py-dev`: +.. _`development mailing list`: +.. _`py-dev developers list`: http://codespeak.net/mailman/listinfo/py-dev +.. _`subversion commit mailing list`: +.. _`py-svn`: +.. _`py-svn general commit mailing list`: http://codespeak.net/mailman/listinfo/py-svn .. _`development bug/feature tracker`: https://codespeak.net/issue/py-dev/ Copied: py/trunk/py/doc/download.txt (from r38451, py/trunk/py/doc/getting-started.txt) ============================================================================== --- py/trunk/py/doc/getting-started.txt (original) +++ py/trunk/py/doc/download.txt Sun Feb 11 14:26:03 2007 @@ -1,31 +1,32 @@ -Getting started with the py lib -=============================== +Download and Installation of the py lib +=============================================== .. contents:: .. sectnum:: -Obtaining the current py lib -============================ +Downloading a tar/zip file and installing it +=================================================== -Due to the nature of its innovative goals `the py lib`_ can't be -easily released without a certain API consistency. Nevertheless, -the API is pretty stable in many respects and very -well tested. So we invite you to participate and -use it - especially if you share `the frustrations with -current python package development`_. +The latest stable public release: -.. _`the py lib`: index.html + `download py-0.9.0.tgz`_ + `download py-0.9.0.zip`_ -getting it via subversion -------------------------- +.. _`download py-0.9.0.tgz`: http://codespeak.net/download/py/py-0.9.0.tgz +.. _`download py-0.9.0.zip`: http://codespeak.net/download/py/py-0.9.0.zip -Checkout the py lib distribution tree with subversion, e.g. use:: +The py lib can be `globally installed via setup.py`_ +or `used locally`_. - svn co http://codespeak.net/svn/py/dist py-dist -to checkout the code, documentation, tool and example tree -into a ``py-dist`` checkout directory. Your naming desire may vary -for your local checkout directory. +Getting (and updating) via subversion +-------------------------------------------- + +Use Subversion to checkout the latest 0.9.x stable release: + + svn co http://codespeak.net/svn/py/release/0.9.x py-0.9.x + +to obtain the complete code and documentation tree. If you experience problems with the subversion checkout e.g. because you have a http-proxy in between that doesn't proxy @@ -33,14 +34,34 @@ of just "codespeak.net". Alternatively, you may tweak your local subversion installation. -setting it up -------------- +If you want to follow stable snapshots +then you may use the equivalent of this invocation: + + svn co http://codespeak.net/svn/py/dist py-dist + + +.. _`globally installed via setup.py`: + +Installation via setup.py +------------------------------ + +Go to your unpacked/checked out directory +and issue: + + python setup.py install + + +.. _`used locally`: + +Local Installation/Usage +------------------------------ You need to put the checkout-directory into your ``PYTHONPATH`` and you want to have the ``py-dist/py/bin/py.test`` script in -your system path, which lets you execute test files and directories. +your (unixish) system path, which lets you execute test files +and directories. -There already is a convenient way for Bash/Shell based systems +There is a convenient way for Bash/Shell based systems to setup the ``PYTHONPATH`` as well as the shell ``PATH``, insert:: eval `python ~/path/to/py-dist/py/env.py` @@ -48,80 +69,45 @@ into your ``.bash_profile``. Of course, you need to specify your own checkout-directory. -If you know of a good developer-style way of doing the -equivalent on win32 (non-cygwin) environments, tell us_. - -And no, we don't yet provide a distutils-install until -we have settled on a convenient way to upgrade seamlessly -via an `svn up` while at the same time allowing -installs/upgrades via the distutils `setup.py` way. -Our `releasescheme document`_ holds some preliminary -planning on how future releaes of the py lib will -look like. - -.. _`releasescheme document`: releasescheme.html - -upgrading it ------------- -Well, easy. Go to your checkout directory and issue:: +.. _`svn-external scenario`: - svn up +The py lib as an svn external +------------------------------------------------------- -have fun and `get an account`_ :-) +Add the py lib as an external to your project `DIRECTORY` +which contains your svn-controlled root package:: + svn propedit 'svn:externals' DIRECTORY -Participating in development -============================ +which will open an editor where you can add +the following line: -The py-dev and py-svn mailing lists ------------------------------------ + py http://codespeak.net/svn/py/dist -If you feel the desire to help tackle bugs and fixes, -or support resolution of some `frustrations`_ or to -just lurk in then please subscribe to one or both -of our mailinglists: +This will make your projcet automatically use the +most recent stable snapshot of the py lib. - `py-dev developers list`_ +Alternatively you may use this url for +integrating the development version: -and our + http://codespeak.net/svn/py/trunk - `py-svn general commit mailing list`_ +or the next one for following the e.g. the 0.9 release branch -Coding and communication ------------------------- + http://codespeak.net/svn/py/release/py-0.9.x -We are practicing what could be called documentation, -vision, discussion and automated test driven development. -In the `future`_ book we try to layout visions and ideas for -the near coding feature to give a means for preliminary -feedback before code hits the ground. -With our `coding style`_ we are mostly following -cpython guidance with some additional restrictions -some of which projects like twisted_ or zope3_ have -adopted in similar ways. +py subversion directory structure +================================= -.. _`zope3`: http://zope3.zwiki.org/ -.. _twisted: http://www.twistedmatrix.org -.. _future: future/future.html +The directory release layout of the repository is +going to follow this scheme:: -.. _`get an account`: + http://codespeak.net/ + svn/py/dist # latest stable (may or may not be a release) + svn/py/release/X.Y.Z # release tags and branches + svn/py/trunk # head development / merge point -get an account on codespeak ---------------------------- -codespeak_ is employing a pretty liberal committing scheme. If you know -someone who is active on codespeak already or you are otherwise known in -the community then you will most probably just get access. But even if -you are new to the python developer community you may still get one if -you want to improve things and can be expected to honour the -style of coding and communication. -.. _`coding style`: coding-style.html -.. _`frustrations`: -.. _`the frustrations with current python package development`: why_py.html#frustrations -.. _us: http://codespeak.net/mailman/listinfo/py-dev -.. _codespeak: http://codespeak.net/ -.. _`py-dev developers list`: http://codespeak.net/mailman/listinfo/py-dev -.. _`py-svn general commit mailing list`: http://codespeak.net/mailman/listinfo/py-svn Modified: py/trunk/py/doc/execnet.txt ============================================================================== --- py/trunk/py/doc/execnet.txt (original) +++ py/trunk/py/doc/execnet.txt Sun Feb 11 14:26:03 2007 @@ -218,8 +218,6 @@ gw = py.execnet.SocketGateway('localhost', cls.port) print "initialized socket gateway to port", cls.port -.. _`py API`: api.html - .. [#] There is an interesting emerging `Jail`_ linux technology as well as a host of others, of course. Modified: py/trunk/py/doc/future/future.txt ============================================================================== --- py/trunk/py/doc/future/future.txt (original) +++ py/trunk/py/doc/future/future.txt Sun Feb 11 14:26:03 2007 @@ -327,7 +327,6 @@ .. _`CPython's distutils`: http://www.python.org/dev/doc/devel/lib/module-distutils.html -.. _`getting started`: ../getting-started.html .. _`restructured text`: http://docutils.sourceforge.net/docs/user/rst/quickref.html .. _`python standard library`: http://www.python.org/doc/2.3.4/lib/lib.html .. _`xpython EuroPython 2004 talk`: http://codespeak.net/svn/user/hpk/talks/xpython-talk.txt Deleted: /py/trunk/py/doc/getting-started.txt ============================================================================== --- /py/trunk/py/doc/getting-started.txt Sun Feb 11 14:26:03 2007 +++ (empty file) @@ -1,127 +0,0 @@ -Getting started with the py lib -=============================== - -.. contents:: -.. sectnum:: - -Obtaining the current py lib -============================ - -Due to the nature of its innovative goals `the py lib`_ can't be -easily released without a certain API consistency. Nevertheless, -the API is pretty stable in many respects and very -well tested. So we invite you to participate and -use it - especially if you share `the frustrations with -current python package development`_. - -.. _`the py lib`: index.html - -getting it via subversion -------------------------- - -Checkout the py lib distribution tree with subversion, e.g. use:: - - svn co http://codespeak.net/svn/py/dist py-dist - -to checkout the code, documentation, tool and example tree -into a ``py-dist`` checkout directory. Your naming desire may vary -for your local checkout directory. - -If you experience problems with the subversion checkout e.g. -because you have a http-proxy in between that doesn't proxy -DAV requests you can try to use "codespeak.net:8080" instead -of just "codespeak.net". Alternatively, you may tweak -your local subversion installation. - -setting it up -------------- - -You need to put the checkout-directory into your ``PYTHONPATH`` -and you want to have the ``py-dist/py/bin/py.test`` script in -your system path, which lets you execute test files and directories. - -There already is a convenient way for Bash/Shell based systems -to setup the ``PYTHONPATH`` as well as the shell ``PATH``, insert:: - - eval `python ~/path/to/py-dist/py/env.py` - -into your ``.bash_profile``. Of course, you need to -specify your own checkout-directory. - -If you know of a good developer-style way of doing the -equivalent on win32 (non-cygwin) environments, tell us_. - -And no, we don't yet provide a distutils-install until -we have settled on a convenient way to upgrade seamlessly -via an `svn up` while at the same time allowing -installs/upgrades via the distutils `setup.py` way. -Our `releasescheme document`_ holds some preliminary -planning on how future releaes of the py lib will -look like. - -.. _`releasescheme document`: releasescheme.html - -upgrading it ------------- - -Well, easy. Go to your checkout directory and issue:: - - svn up - -have fun and `get an account`_ :-) - - -Participating in development -============================ - -The py-dev and py-svn mailing lists ------------------------------------ - -If you feel the desire to help tackle bugs and fixes, -or support resolution of some `frustrations`_ or to -just lurk in then please subscribe to one or both -of our mailinglists: - - `py-dev developers list`_ - -and our - - `py-svn general commit mailing list`_ - -Coding and communication ------------------------- - -We are practicing what could be called documentation, -vision, discussion and automated test driven development. -In the `future`_ book we try to layout visions and ideas for -the near coding feature to give a means for preliminary -feedback before code hits the ground. - -With our `coding style`_ we are mostly following -cpython guidance with some additional restrictions -some of which projects like twisted_ or zope3_ have -adopted in similar ways. - -.. _`zope3`: http://zope3.zwiki.org/ -.. _twisted: http://www.twistedmatrix.org -.. _future: future/future.html - -.. _`get an account`: - -get an account on codespeak ---------------------------- - -codespeak_ is employing a pretty liberal committing scheme. If you know -someone who is active on codespeak already or you are otherwise known in -the community then you will most probably just get access. But even if -you are new to the python developer community you may still get one if -you want to improve things and can be expected to honour the -style of coding and communication. - -.. _`coding style`: coding-style.html -.. _`frustrations`: -.. _`the frustrations with current python package development`: why_py.html#frustrations -.. _us: http://codespeak.net/mailman/listinfo/py-dev -.. _codespeak: http://codespeak.net/ -.. _`py-dev developers list`: http://codespeak.net/mailman/listinfo/py-dev -.. _`py-svn general commit mailing list`: http://codespeak.net/mailman/listinfo/py-svn Modified: py/trunk/py/doc/index.txt ============================================================================== --- py/trunk/py/doc/index.txt (original) +++ py/trunk/py/doc/index.txt Sun Feb 11 14:26:03 2007 @@ -1,13 +1,25 @@ -py.test and the py lib - documentation ----------------------------------------- +py lib documentation +================================================= + +`Download and Installation`_ + +Main tools and API +---------------------- `py.test`_ introduces to the **py.test** testing utility -`py.execnet`_ an innovative way to distribute programs across the net (not stable on win32 yet) +`py.execnet`_ distribute programs across the net + +`py.magic.greenlet`_: micro-threads (lightweight in-process concurrent programming) -`py.magic.greenlet`_: Lightweight in-process concurrent programming (aka Stackless) +`py.path`_: local and subversion Path and Filesystem access + +`py lib scripts`_ describe the scripts contained in the ``py/bin`` directory. -`py.path`_: Path and Filesystem access and manipulation for local and svn based trees +`apigen`_: a new way to generate rich Python API documentation + +support functionality +--------------------------------- `py.code`_: High-level access/manipulation of Python code and traceback objects. @@ -17,23 +29,20 @@ `py.log`_ an alpha document about the ad-hoc logging facilities -`py lib scripts`_ describe the scripts contained in the ``py/bin`` directory. +`miscellaneous features`_ describes some small but nice py lib features -`miscellaneous features`_ describes some more py lib features +Background and Motivation information +------------------------------------------- `future`_ handles development visions and plans for the near future. `why what how py?`_, describing motivation and background of the py lib -Note that some parts of these texts refer to future development and -do not reflect the current state. **Welcome to documentation and -test driven development** :-) - - -.. _`getting started`: getting-started.html +.. _`download and installation`: download.txt .. _`py-dev at codespeak net`: http://codespeak.net/mailman/listinfo/py-dev .. _`py.execnet`: execnet.html .. _`py.magic.greenlet`: greenlet.html +.. _`apigen`: apigen.html .. _`py.log`: log.html .. _`py.io`: io.html .. _`py.path`: path.html @@ -43,6 +52,5 @@ .. _`py.xml`: xml.html .. _`Why What how py?`: why_py.html .. _`future`: future/future.html -.. _`getting started`: getting-started.html .. _`miscellaneous features`: misc.html Deleted: /py/trunk/py/doc/releasescheme.txt ============================================================================== --- /py/trunk/py/doc/releasescheme.txt Sun Feb 11 14:26:03 2007 +++ (empty file) @@ -1,113 +0,0 @@ - -py subversion directory structure -================================= - -The directory release layout of the repository is -going to follow this scheme:: - - svn/py/dist # latest stable code base (may or may not be a release) - svn/py/release/X.Y.Z # tagged releases - svn/py/branch/X.Y # contains release branch development - svn/py/trunk # head development - -Scenario "no svn and just let me play, please" -============================================== - -If you don't have a subversion client you can download -specific versions by going to - - http://codespeak.net/download/py - -and pick a suitable archive file. You need to -unpack it and may want to run `setup.py` to install -it in a system-wide manner. - -Installation Scenario "svn + stay close to released versions" -------------------------------------------------------------- - -If you have a subversion client (you can easily install -one, look at PyPy's `svn help document`_) it is recommended that you choose the following -method to install, irrespective if you want to install -things via distutils. Start by issueing the following -shell command (or a graphical equivalent):: - - svn co http://codespeak.net/svn/py/dist py-dist - -Now we want to make sure that the `py-dist/py/bin` directory -gets on your shell search path in order to have "py.test" -directly available. You basically have two choices: - -1) include in your local shell environment startup the equivalent - of ``eval `python .../py-dist/py/env.py```. - XXX describe this in more detail (rip out from `getting started`_). - -2) go to the `py-dist` directory and run `python setup.py install`. - -.. _`getting started`: getting-started.html -.. _`svn help document`: http://codespeak.net/pypy/index.cgi?doc/getting_started.html#subversion - -If you later want to upgrade your version of the py lib -to the newest release you simply issue:: - - svn up - -or to switch to the development trunk via:: - - svn switch http://codespeak.net/svn/py/trunk - -or to a specific version via:: - - svn switch http://codespeak.net/svn/py/tag/py-X.Y.Z - -or to a specific release branch:: - - svn switch http://codespeak.net/svn/py/branch/py-X.Y - -If you choose the option No. 2) above you have to repeat -the distutils install after each checkout/switch. - -.. _`svn-external scenario`: - - -Installation Scenario "svn + include py lib as an external" -=========================================================== - -OK, so you want to have the py lib supporting your -application and are using subversion? Great because -things are quite easy and flexible for this scenario. - -Tying the py lib into your subversion controled project -------------------------------------------------------- - -On the `DIRECTORY` which contains your root package issue:: - - svn pe 'svn:externals' DIRECTORY - -and add the following line to your (possibly empty) list -of svn-externals:: - - py http://codespeak.net/svn/py/dist - -This will make your projcet follow the most recent -release of the py lib. (please substitute `dist` for `trunk` -if you want to follow py lib development, this will let -you catch interaction problems early on ...). -If you now issue an `svn up` on your `DIRECTORY` you -will retrieve the external into your application. - -If you want to follow a minor release branch seamlessly -then use the following line:: - - py http://codespeak.net/svn/py/branch/py-X.Y - -where `X.Y` indicate the branch you want to follow. - -If you want to use a very fixed version of the py lib -you can tie to a specific release:: - - py http://codespeak.net/svn/py/tag/py-X.Y.Z - -Integrating the py lib into your distribution ----------------------------------------------- - -XXX Modified: py/trunk/py/doc/test.txt ============================================================================== --- py/trunk/py/doc/test.txt (original) +++ py/trunk/py/doc/test.txt Sun Feb 11 14:26:03 2007 @@ -14,11 +14,12 @@ starting point: ``py.test`` command line tool ============================================= -First, see `getting started`_ for how to install the 'py.test' tool -on your system. +We presume you have done an installation as per the +download_ page after which you should be able to execute the +'py.test' tool from a command line shell. ``py.test`` is the command line tool to run tests. You can supply it -with any Python module by passing it as an argument:: +with a Python test file (or directory) by passing it as an argument:: py.test test_sample.py @@ -32,11 +33,9 @@ def test_answer(): assert 42 == 43 -As you can see, you can have test functions as well as test -methods. This in contrast to the Python standard library's -``unittest.py``. - -You can use ``py.test`` to run all tests in a directory structure by +You may have test functions and test methods, there is no +need to subclass or to put tests into a class. +You can also use ``py.test`` to run all tests in a directory structure by invoking it without any arguments:: py.test @@ -46,9 +45,7 @@ subdirectories, starting with the current directory, and run them. Each Python test module is inspected for test methods starting with ``test_``. -.. _`getting started`: getting-started.html - - +.. _download: download.html .. _features: Basic Features of ``py.test`` Modified: py/trunk/py/doc/why_py.txt ============================================================================== --- py/trunk/py/doc/why_py.txt (original) +++ py/trunk/py/doc/why_py.txt Sun Feb 11 14:26:03 2007 @@ -127,7 +127,7 @@ completly change the implementation but the public API tests will continue to run. -- No **tested feature** of the exported `py API`_ is to vanish +- No **tested feature** of the exported py API is to vanish across minor releases until it is marked deprecated. For example, pure API tests of a future version 1.0 are to @@ -269,7 +269,6 @@ .. _`envisioned import/export system`: future/future.html#importexport .. _future: future/future.html .. _`py.test tool and library`: test.html -.. _`py API`: api.html .. _`great work that Fred L. Drake, Jr. is doing`: http://www.python.org/doc/2.3.4/lib/lib.html -- From hpk at codespeak.net Sun Feb 11 14:31:25 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 11 Feb 2007 14:31:25 +0100 (CET) Subject: [py-svn] r38456 - py/trunk/py/apigen Message-ID: <20070211133125.929BA1009C@code0.codespeak.net> Author: hpk Date: Sun Feb 11 14:31:24 2007 New Revision: 38456 Modified: py/trunk/py/apigen/todo.txt Log: konqueror issues Modified: py/trunk/py/apigen/todo.txt ============================================================================== --- py/trunk/py/apigen/todo.txt (original) +++ py/trunk/py/apigen/todo.txt Sun Feb 11 14:31:24 2007 @@ -1,4 +1,7 @@ +* get konqueror to display indents in source code better? + (currently it doesn't look like more than a single space) + * source page headers should read: py/apigen sources [rev XXX] From hpk at codespeak.net Sun Feb 11 14:33:23 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 11 Feb 2007 14:33:23 +0100 (CET) Subject: [py-svn] r38458 - py/trunk/py/misc Message-ID: <20070211133323.6EB5F1009C@code0.codespeak.net> Author: hpk Date: Sun Feb 11 14:33:22 2007 New Revision: 38458 Modified: py/trunk/py/misc/std.py Log: better docstring for top level object Modified: py/trunk/py/misc/std.py ============================================================================== --- py/trunk/py/misc/std.py (original) +++ py/trunk/py/misc/std.py Sun Feb 11 14:33:22 2007 @@ -2,7 +2,9 @@ import sys class Std(object): - """ (lazily) hook into the top-level standard library """ + """ makes all standard python modules available as a lazily + computed attribute. + """ def __init__(self): self.__dict__ = sys.modules From hpk at codespeak.net Sun Feb 11 14:45:50 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 11 Feb 2007 14:45:50 +0100 (CET) Subject: [py-svn] r38459 - py/trunk/py/test Message-ID: <20070211134550.4CB6E10089@code0.codespeak.net> Author: hpk Date: Sun Feb 11 14:45:49 2007 New Revision: 38459 Modified: py/trunk/py/test/deprecate.py Log: two functions with the same name, use the latter one. Modified: py/trunk/py/test/deprecate.py ============================================================================== --- py/trunk/py/test/deprecate.py (original) +++ py/trunk/py/test/deprecate.py Sun Feb 11 14:45:49 2007 @@ -1,27 +1,9 @@ import py -def deprecated_call(func, *args, **kwargs): +def deprecated_call(func, *args, **kwargs): """ assert that calling func(*args, **kwargs) triggers a DeprecationWarning. """ - oldfilters = py.std.warnings.filters[:] - onceregistry = py.std.warnings.onceregistry.copy() - try: - py.std.warnings.onceregistry.clear() - py.std.warnings.filterwarnings("error", category=DeprecationWarning) - try: - _ = func(*args, **kwargs) - except DeprecationWarning: - pass - else: - print __warningregistry__ - raise AssertionError("%s not deprecated" % (func,)) - finally: - py.std.warnings.filters[:] = oldfilters - py.std.warnings.onceregistry.clear() - py.std.warnings.onceregistry.update(onceregistry) - -def deprecated_call(func, *args, **kwargs): l = [] oldwarn = py.std.warnings.warn_explicit def warn_explicit(*args, **kwargs): From hpk at codespeak.net Sun Feb 11 14:46:48 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 11 Feb 2007 14:46:48 +0100 (CET) Subject: [py-svn] r38460 - in py/trunk/py: apigen/source apigen/tracer/testing misc/testing test/rsession/testing test/testing Message-ID: <20070211134648.A988210089@code0.codespeak.net> Author: hpk Date: Sun Feb 11 14:46:46 2007 New Revision: 38460 Removed: py/trunk/py/apigen/tracer/testing/test_magic.py Modified: py/trunk/py/apigen/source/server.py py/trunk/py/misc/testing/test_initpkg.py py/trunk/py/test/rsession/testing/test_web.py py/trunk/py/test/rsession/testing/test_webjs.py py/trunk/py/test/testing/test_remote.py Log: removing a very-likely-obsolete test and otherwise fixing files and tests to do py.test.skip()s only during setup. Modified: py/trunk/py/apigen/source/server.py ============================================================================== --- py/trunk/py/apigen/source/server.py (original) +++ py/trunk/py/apigen/source/server.py Sun Feb 11 14:46:46 2007 @@ -3,10 +3,7 @@ """ import py -try: - from pypy.translator.js.examples import server -except ImportError: - py.test.skip("PyPy not found") +from pypy.translator.js.examples import server from py.__.apigen.source.browser import parse_path from py.__.apigen.source.html import create_html, create_dir_html, create_unknown_html from py.xml import html Deleted: /py/trunk/py/apigen/tracer/testing/test_magic.py ============================================================================== --- /py/trunk/py/apigen/tracer/testing/test_magic.py Sun Feb 11 14:46:46 2007 +++ (empty file) @@ -1,48 +0,0 @@ - -""" test magic abilities of tracer -""" - -import py -py.test.skip("These features have been disabled") - -from py.__.apigen.tracer.magic import trace, get_storage, stack_copier, \ - DocStorageKeeper -from py.__.apigen.tracer.docstorage import DocStorage -from py.__.apigen.tracer import model - -#def setup_function(f): -# DocStorageKeeper.set_storage(DocStorage().from_dict({})) - -def fun(a, b, c): - return "a" -fun = trace()(fun) - -def test_magic(): - fun(1, 2, 3) - - ds = get_storage() - assert 'fun' in ds.descs - assert len(ds.descs.keys()) == 2 - desc = ds.descs['fun'] - inputcells = desc.inputcells - assert isinstance(inputcells[0], model.SomeInt) - assert isinstance(inputcells[1], model.SomeInt) - assert isinstance(inputcells[2], model.SomeInt) - assert isinstance(desc.retval, model.SomeString) - -def g(x): - return f(x) - -def f(x): - return x + 3 -f = trace(keep_frames=True, frame_copier=stack_copier)(f) - -def test_fancy_copier(): - g(1) - - ds = get_storage() - assert 'f' in ds.descs - desc = ds.descs['f'] - stack = desc.call_sites.values()[0][0] - assert str(stack[0].statement) == ' return f(x)' - assert str(stack[1].statement) == ' g(1)' Modified: py/trunk/py/misc/testing/test_initpkg.py ============================================================================== --- py/trunk/py/misc/testing/test_initpkg.py (original) +++ py/trunk/py/misc/testing/test_initpkg.py Sun Feb 11 14:46:46 2007 @@ -52,6 +52,8 @@ base.join('test', 'testing', 'data'), base.join('apigen', 'tracer', 'testing', 'package'), base.join('test', 'testing', 'test'), + base.join('test', 'rsession', 'webjs.py'), + base.join('apigen', 'source', 'server.py'), base.join('magic', 'greenlet.py'), base.join('path', 'gateway',), base.join('doc',), Modified: py/trunk/py/test/rsession/testing/test_web.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_web.py (original) +++ py/trunk/py/test/rsession/testing/test_web.py Sun Feb 11 14:46:46 2007 @@ -4,15 +4,15 @@ import py -try: - from pypy.translator.js.main import rpython2javascript - from pypy.translator.js import commproxy - - commproxy.USE_MOCHIKIT = False -except ImportError: - py.test.skip("PyPy not found") - def setup_module(mod): + try: + from pypy.translator.js.main import rpython2javascript + from pypy.translator.js import commproxy + except ImportError: + py.test.skip("PyPy not found") + mod.commproxy.USE_MOCHIKIT = False + mod.rpython2javascript = rpython2javascript + mod.commproxy = mod.commproxy from py.__.test.rsession.web import TestHandler as _TestHandler from py.__.test.rsession.web import MultiQueue mod._TestHandler = _TestHandler Modified: py/trunk/py/test/rsession/testing/test_webjs.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_webjs.py (original) +++ py/trunk/py/test/rsession/testing/test_webjs.py Sun Feb 11 14:46:46 2007 @@ -1,19 +1,26 @@ import py -try: - import pypy - from pypy.translator.js.modules import dom - from pypy.translator.js.tester import schedule_callbacks - dom.Window # check whether dom was properly imported or is just a - # leftover in sys.modules -except (ImportError, AttributeError): - py.test.skip('PyPy not found') - -from py.__.test.rsession import webjs -from py.__.test.rsession.web import exported_methods -here = py.magic.autopath().dirpath() +def check(mod): + try: + import pypy + from pypy.translator.js.modules import dom + from pypy.translator.js.tester import schedule_callbacks + dom.Window # check whether dom was properly imported or is just a + # leftover in sys.modules + except (ImportError, AttributeError): + py.test.skip('PyPy not found') + mod.dom = dom + mod.schedule_callbacks = schedule_callbacks + + from py.__.test.rsession import webjs + from py.__.test.rsession.web import exported_methods + mod.webjs = webjs + mod.exported_methods = exported_methods + mod.here = py.magic.autopath().dirpath() def setup_module(mod): + check(mod) + # load HTML into window object html = here.join('../webdata/index.html').read() mod.html = html Modified: py/trunk/py/test/testing/test_remote.py ============================================================================== --- py/trunk/py/test/testing/test_remote.py (original) +++ py/trunk/py/test/testing/test_remote.py Sun Feb 11 14:46:46 2007 @@ -38,7 +38,7 @@ pool = py._thread.WorkerPool() reply = pool.dispatch(session.main) while 1: - s = out.get(timeout=1.0) + s = out.get(timeout=5.0) if s.find('1 failed') != -1: break print s @@ -46,7 +46,7 @@ py.test.fail("did not see test_1 failure") # XXX we would like to have a cleaner way to finish try: - reply.get(timeout=0.5) + reply.get(timeout=5.0) except IOError, e: assert str(e).lower().find('timeout') != -1 From hpk at codespeak.net Sun Feb 11 15:59:57 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 11 Feb 2007 15:59:57 +0100 (CET) Subject: [py-svn] r38467 - in py/trunk/py/path/local: . testing Message-ID: <20070211145957.AF3E310089@code0.codespeak.net> Author: hpk Date: Sun Feb 11 15:59:56 2007 New Revision: 38467 Modified: py/trunk/py/path/local/local.py py/trunk/py/path/local/testing/test_win.py Log: make string comparison lowercase-insensitive for windows Modified: py/trunk/py/path/local/local.py ============================================================================== --- py/trunk/py/path/local/local.py (original) +++ py/trunk/py/path/local/local.py Sun Feb 11 15:59:56 2007 @@ -10,7 +10,9 @@ import py from py.__.path import common -if sys.platform == 'win32': +iswin32 = sys.platform == "win32" + +if iswin32: from py.__.path.local.win import WinMixin as PlatformMixin else: from py.__.path.local.posix import PosixMixin as PlatformMixin @@ -193,7 +195,12 @@ return obj def __eq__(self, other): - return str(self) == str(other) + s1 = str(self) + s2 = str(other) + if iswin32: + s1 = s1.lower() + s2 = s2.lower() + return s1 == s2 def open(self, mode='r'): """ return an opened file with the given mode. """ Modified: py/trunk/py/path/local/testing/test_win.py ============================================================================== --- py/trunk/py/path/local/testing/test_win.py (original) +++ py/trunk/py/path/local/testing/test_win.py Sun Feb 11 15:59:56 2007 @@ -24,6 +24,12 @@ self.root.chmod(mode) assert self.root.stat().st_mode == mode + def test_path_comparison_lowercase_mixed(self): + t1 = self.root.join("a_path") + t2 = self.root.join("A_path") + assert t1 == t1 + assert t1 == t2 + def test_allow_unix_style_paths(self): t1 = self.root.join('a_path') assert t1 == str(self.root) + '\\a_path' From hpk at codespeak.net Sun Feb 11 16:11:50 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 11 Feb 2007 16:11:50 +0100 (CET) Subject: [py-svn] r38470 - in py/trunk/py/process: . testing Message-ID: <20070211151150.B7DF810089@code0.codespeak.net> Author: hpk Date: Sun Feb 11 16:11:49 2007 New Revision: 38470 Modified: py/trunk/py/process/cmdexec.py py/trunk/py/process/testing/test_cmdexec.py Log: try to have py.process.cmdexec run on top of PyPy (with its currentl limitations) Modified: py/trunk/py/process/cmdexec.py ============================================================================== --- py/trunk/py/process/cmdexec.py (original) +++ py/trunk/py/process/cmdexec.py Sun Feb 11 16:11:49 2007 @@ -124,6 +124,9 @@ if '"' in cmd and not cmd.startswith('""'): cmd = '"%s"' % cmd + return popen3_exec_cmd(cmd) + +def popen3_exec_cmd(cmd): stdin, stdout, stderr = os.popen3(cmd) out = stdout.read() err = stderr.read() @@ -134,6 +137,8 @@ raise ExecutionFailed(status, status, cmd, out, err) return out +def pypy_exec_cmd(cmd): + return popen3_exec_cmd(cmd) class ExecutionFailed(py.error.Error): def __init__(self, status, systemstatus, cmd, out, err): @@ -149,8 +154,11 @@ # # choose correct platform-version # + if sys.platform == 'win32': cmdexec = win32_exec_cmd +elif hasattr(sys, 'pypy') or hasattr(sys, 'pypy_objspaceclass'): + cmdexec = popen3_exec_cmd else: cmdexec = posix_exec_cmd Modified: py/trunk/py/process/testing/test_cmdexec.py ============================================================================== --- py/trunk/py/process/testing/test_cmdexec.py (original) +++ py/trunk/py/process/testing/test_cmdexec.py Sun Feb 11 16:11:49 2007 @@ -1,4 +1,4 @@ -from py import test +import py from py.process import cmdexec class Test_exec_cmd: @@ -7,7 +7,7 @@ assert out.strip() == 'hallo' def test_simple_error(self): - test.raises (cmdexec.Error, cmdexec, 'exit 1') + py.test.raises (cmdexec.Error, cmdexec, 'exit 1') def test_simple_error_exact_status(self): try: @@ -23,3 +23,16 @@ assert hasattr(e, 'err') assert hasattr(e, 'out') assert e.err or e.out + +def test_cmdexec_selection(): + from py.__.process import cmdexec + if py.std.sys.platform == "win32": + assert py.process.cmdexec == cmdexec.win32_exec_cmd + elif hasattr(py.std.sys, 'pypy') or hasattr(py.std.sys, 'pypy_objspaceclass'): + assert py.process.cmdexec == cmdexec.popen3_exec_cmd + else: + assert py.process.cmdexec == cmdexec.posix_exec_cmd + + + + From hpk at codespeak.net Sun Feb 11 17:14:41 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 11 Feb 2007 17:14:41 +0100 (CET) Subject: [py-svn] r38472 - py/trunk/py/doc Message-ID: <20070211161441.32A8D10097@code0.codespeak.net> Author: hpk Date: Sun Feb 11 17:14:40 2007 New Revision: 38472 Modified: py/trunk/py/doc/test.txt Log: clarifications regarding dist testing Modified: py/trunk/py/doc/test.txt ============================================================================== --- py/trunk/py/doc/test.txt (original) +++ py/trunk/py/doc/test.txt Sun Feb 11 17:14:40 2007 @@ -530,9 +530,9 @@ Differences from local tests ---------------------------- -* Test order is *not* guaranteed. -* Hanging nodes or tests are not detected properly. -* ``conftest.py`` cannot reference files outside of the copied packages. +* Test order is rather random (instead of in file order). +* the test process may hang due to network problems +* you may not reference files outside of rsynced directory structures Configuration ------------- From hpk at codespeak.net Sun Feb 11 17:24:19 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 11 Feb 2007 17:24:19 +0100 (CET) Subject: [py-svn] r38473 - py/trunk/py/doc Message-ID: <20070211162419.51EE110097@code0.codespeak.net> Author: hpk Date: Sun Feb 11 17:24:17 2007 New Revision: 38473 Modified: py/trunk/py/doc/test.txt Log: rewrote the future section of py test Modified: py/trunk/py/doc/test.txt ============================================================================== --- py/trunk/py/doc/test.txt (original) +++ py/trunk/py/doc/test.txt Sun Feb 11 17:24:17 2007 @@ -580,59 +580,47 @@ Future/Planned Features of py.test ================================== -Please note that the following descriptions of future features -sound factual although they aren't implemented yet. This -allows easy migration to real documentation later. -Nevertheless, none of the described planned features is -set in stone, yet. In fact, they are open to discussion on -py-dev at codespeak dot net. - -Hey, if you want to suggest new features or command line options -for py.test it would be great if you could do it by providing -documentation for the feature. Welcome to documentation driven -development :-) - -selecting tests by queries/full text search +integrating various test methods ------------------------------------------- - Note: there already is experimental support for test `selection by keyword`_. - Otherwise the following is not yet implemented - -You can selectively run tests by specifiying words -on the command line in a google-like way. Example:: - - py.test simple - -will run all tests that are found from the current directory -and that have the word "simple" somewhere in their `test address`_. -``test_simple1`` and ``TestSomething.test_whatever_simpleton`` would both -qualify. If you want to exclude the latter test method you could say:: - - py.test -- simple -simpleton - -Note that the doubledash "--" signals the end of option parsing so -that "-simpleton" will not be misinterpreted as a command line option. - -Interpreting positional arguments as specifying search queries -means that you can only restrict the set of tests. There is no way to -say "run all 'simple' in addition to all 'complex' tests". If this proves -to be a problem we can probably come up with a command line option -that allows to specify multiple queries which all add to the set of -tests-to-consider. - -.. _`test address`: - -the concept of a test address ------------------------------ - -For specifiying tests it is convenient to define the notion -of a *test address*, representable as a filesystem path and a -list of names leading to a test item. If represented as a single -string the path and names are separated by a `/` character, for example: - - ``somedir/somepath.py/TestClass/test_method`` - -Such representations can be used to memoize failing tests -by writing them out in a file or communicating them across -process and computer boundaries. +There are various conftest.py's out there +that do html-reports, ad-hoc distribute tests +to windows machines or other fun stuff. +These approaches should be offerred natively +by py.test at some point (requires refactorings). +In addition, performing special checks such +as w3c-conformance tests or ReST checks +should be offered from mainline py.test. + +more distributed testing +----------------------------------------- + +We'd like to generalize and extend our ad-hoc +distributed testing approach to allow for running +on multiple platforms simultanously and selectively. +The web reporter should learn to deal with driving +complex multi-platform test runs and providing +useful introspection and interactive debugging hooks. + + +move to report event based architecture +-------------------------------------------- + +To facilitate writing of custom reporters +py.test is to learn to generate reporting events +at all levels which a reporter can choose to +interpret and present. The distributed testing +approach already uses such an approach and +we'd like to unify this with the default +in-process py.test mode. + + +see what other tools do currently (nose, etc.) +---------------------------------------------------- + +There are various tools out there, among them +the nose_ clone. It's about time to look again +at these and other tools, integrate interesting +features and maybe collaborate on some issues. +.. _nose: http://somethingaboutorange.com/mrl/projects/nose/ From hpk at codespeak.net Sun Feb 11 17:25:03 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 11 Feb 2007 17:25:03 +0100 (CET) Subject: [py-svn] r38474 - in py/trunk/py/test: . testing Message-ID: <20070211162503.7BC0910075@code0.codespeak.net> Author: hpk Date: Sun Feb 11 17:25:01 2007 New Revision: 38474 Modified: py/trunk/py/test/collect.py py/trunk/py/test/config.py py/trunk/py/test/defaultconftest.py py/trunk/py/test/item.py py/trunk/py/test/testing/test_config.py py/trunk/py/test/testing/test_session.py Log: make io capturing configurable per e.g. conf_iocapture = "sys" by default it has "fd" for performing fd-based capturing Modified: py/trunk/py/test/collect.py ============================================================================== --- py/trunk/py/test/collect.py (original) +++ py/trunk/py/test/collect.py Sun Feb 11 17:25:01 2007 @@ -362,15 +362,10 @@ return res def startcapture(self): - if not self._config.option.nocapture: - assert not hasattr(self, '_capture') - self._capture = py.io.StdCaptureFD() + self._config._startcapture(self, path=self.fspath) def finishcapture(self): - if hasattr(self, '_capture'): - capture = self._capture - del self._capture - self._captured_out, self._captured_err = capture.reset() + self._config._finishcapture(self) def __repr__(self): return "<%s %r>" % (self.__class__.__name__, self.name) Modified: py/trunk/py/test/config.py ============================================================================== --- py/trunk/py/test/config.py (original) +++ py/trunk/py/test/config.py Sun Feb 11 17:25:01 2007 @@ -252,6 +252,24 @@ %(chain[0], self.topdir)) return relpath, tuple([x.name for x in chain[1:]]) + def _startcapture(self, colitem, path=None): + if not self.option.nocapture: + assert not hasattr(colitem, '_capture') + iocapture = self.getvalue("conf_iocapture", path=path) + if iocapture == "fd": + capture = py.io.StdCaptureFD() + elif iocapture == "sys": + capture = py.io.StdCapture() + else: + raise ValueError("unknown io capturing: " + iocapture) + colitem._capture = capture + + def _finishcapture(self, colitem): + if hasattr(colitem, '_capture'): + capture = colitem._capture + del colitem._capture + colitem._captured_out, colitem._captured_err = capture.reset() + # this is the one per-process instance of py.test configuration config_per_process = Config() Modified: py/trunk/py/test/defaultconftest.py ============================================================================== --- py/trunk/py/test/defaultconftest.py (original) +++ py/trunk/py/test/defaultconftest.py Sun Feb 11 17:25:01 2007 @@ -8,8 +8,7 @@ Function = py.test.Function Instance = py.test.collect.Instance -additionalinfo = None - +conf_iocapture = "fd" # overridable from conftest.py # =================================================== # Distributed testing specific options Modified: py/trunk/py/test/item.py ============================================================================== --- py/trunk/py/test/item.py (original) +++ py/trunk/py/test/item.py Sun Feb 11 17:25:01 2007 @@ -32,15 +32,10 @@ class Item(py.test.collect.Collector): def startcapture(self): - if not self._config.option.nocapture: - self._capture = py.io.StdCaptureFD() + self._config._startcapture(self, path=self.fspath) def finishcapture(self): - if hasattr(self, '_capture'): - capture = self._capture - del self._capture - self._captured_out, self._captured_err = capture.reset() - + self._config._finishcapture(self) class Function(Item): """ a Function Item is responsible for setting up Modified: py/trunk/py/test/testing/test_config.py ============================================================================== --- py/trunk/py/test/testing/test_config.py (original) +++ py/trunk/py/test/testing/test_config.py Sun Feb 11 17:25:01 2007 @@ -289,6 +289,30 @@ assert pl[0] == tmpdir assert pl[1] == somepath + def test_config_iocapturing(self): + self.tmpdir + config = py.test.config._reparse([self.tmpdir]) + assert config.getvalue("conf_iocapture") + tmpdir = self.tmpdir.ensure("sub-with-conftest", dir=1) + tmpdir.join("conftest.py").write(py.code.Source(""" + conf_iocapture = "sys" + """)) + config = py.test.config._reparse([tmpdir]) + assert config.getvalue("conf_iocapture") == "sys" + class dummy: pass + config._startcapture(dummy) + print 42 + py.std.os.write(1, "23") + config._finishcapture(dummy) + assert dummy._captured_out.strip() == "42" + + config = py.test.config._reparse([tmpdir.dirpath()]) + config._startcapture(dummy, path=tmpdir) + print 42 + py.std.os.write(1, "23") + config._finishcapture(dummy) + assert dummy._captured_out.strip() == "42" + class TestConfigColitems: def setup_class(cls): cls.tmproot = py.test.ensuretemp(cls.__name__) Modified: py/trunk/py/test/testing/test_session.py ============================================================================== --- py/trunk/py/test/testing/test_session.py (original) +++ py/trunk/py/test/testing/test_session.py Sun Feb 11 17:25:01 2007 @@ -194,10 +194,10 @@ import py class Function(py.test.Function): def startcapture(self): - self._mycapture = py.io.StdCaptureFD() + self._mycapture = None def finishcapture(self): - self._testmycapture = self._mycapture.reset() + self._testmycapture = None """)) session = self.mainsession(o) l = session.getitemoutcomepairs(Passed) @@ -205,9 +205,6 @@ item = l[0][0] assert hasattr(item, '_testmycapture') print item._testmycapture - out, err = item._testmycapture - assert int(out.strip()) == 42 - assert int(err.strip()) == 23 assert isinstance(item.parent, py.test.collect.Module) out, err = item.parent._getouterr() From hpk at codespeak.net Sun Feb 11 17:56:46 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 11 Feb 2007 17:56:46 +0100 (CET) Subject: [py-svn] r38480 - py/trunk/py/io Message-ID: <20070211165646.067441007B@code0.codespeak.net> Author: hpk Date: Sun Feb 11 17:56:46 2007 New Revision: 38480 Modified: py/trunk/py/io/fdcapture.py Log: unneeded import Modified: py/trunk/py/io/fdcapture.py ============================================================================== --- py/trunk/py/io/fdcapture.py (original) +++ py/trunk/py/io/fdcapture.py Sun Feb 11 17:56:46 2007 @@ -1,7 +1,6 @@ import os import sys -import thread import py class FDCapture: From hpk at codespeak.net Sun Feb 11 18:00:52 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 11 Feb 2007 18:00:52 +0100 (CET) Subject: [py-svn] r38481 - py/dist/py Message-ID: <20070211170052.2EC4510094@code0.codespeak.net> Author: hpk Date: Sun Feb 11 18:00:51 2007 New Revision: 38481 Removed: py/dist/py/ Log: remove for merge-from-trunk From hpk at codespeak.net Sun Feb 11 18:00:58 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 11 Feb 2007 18:00:58 +0100 (CET) Subject: [py-svn] r38482 - py/dist/py Message-ID: <20070211170058.96A9A1009F@code0.codespeak.net> Author: hpk Date: Sun Feb 11 18:00:57 2007 New Revision: 38482 Added: py/dist/py/ - copied from r38481, py/trunk/py/ Log: use new py-trunk From hpk at codespeak.net Sun Feb 11 18:08:15 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 11 Feb 2007 18:08:15 +0100 (CET) Subject: [py-svn] r38484 - in py/trunk/py/test: . rsession/testing testing Message-ID: <20070211170815.BD7F61007B@code0.codespeak.net> Author: hpk Date: Sun Feb 11 18:08:13 2007 New Revision: 38484 Modified: py/trunk/py/test/config.py py/trunk/py/test/rsession/testing/test_master.py py/trunk/py/test/testing/test_config.py Log: make config.option override any conftest provided value. Modified: py/trunk/py/test/config.py ============================================================================== --- py/trunk/py/test/config.py (original) +++ py/trunk/py/test/config.py Sun Feb 11 18:08:13 2007 @@ -34,7 +34,6 @@ usage="usage: %prog [options] [query] [filenames of tests]") self.conftest = Conftest() self._initialized = False - self._overwrite_dict = {} def parse(self, args): """ parse cmdline arguments into this config object. @@ -127,8 +126,8 @@ conftest modules found during command line parsing. """ try: - return self._overwrite_dict[name] - except KeyError: + return getattr(self.option, name) + except AttributeError: return self.conftest.rget(name, path) def initsession(self): @@ -186,11 +185,6 @@ finally: config_per_process = py.test.config = oldconfig - def _overwrite(self, name, value): - """ this is used from tests to overwrite values irrespectives of conftests. - """ - self._overwrite_dict[name] = value - def make_repr(self, conftestnames, optnames=None): """ return a marshallable representation of conftest and cmdline options. Modified: py/trunk/py/test/rsession/testing/test_master.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_master.py (original) +++ py/trunk/py/test/rsession/testing/test_master.py Sun Feb 11 18:08:13 2007 @@ -20,7 +20,7 @@ mod.tmpdir = tmpdir = py.test.ensuretemp(mod.__name__) # to avoid rsyncing config = py.test.config._reparse([tmpdir]) - config._overwrite('dist_taskspernode', 10) + config.option.dist_taskspernode = 10 mod.rootcol = config._getcollector(tmpdir) class DummyGateway(object): Modified: py/trunk/py/test/testing/test_config.py ============================================================================== --- py/trunk/py/test/testing/test_config.py (original) +++ py/trunk/py/test/testing/test_config.py Sun Feb 11 18:08:13 2007 @@ -101,7 +101,7 @@ o.ensure("conftest.py").write("x=1") config = py.test.config._reparse([str(o)]) assert config.getvalue('x') == 1 - config._overwrite('x', 2) + config.option.x = 2 assert config.getvalue('x') == 2 config = py.test.config._reparse([str(o)]) assert config.getvalue('x') == 1 From hpk at codespeak.net Sun Feb 11 18:13:46 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 11 Feb 2007 18:13:46 +0100 (CET) Subject: [py-svn] r38485 - py/dist/py Message-ID: <20070211171346.0D8211007B@code0.codespeak.net> Author: hpk Date: Sun Feb 11 18:13:45 2007 New Revision: 38485 Removed: py/dist/py/ Log: remove for merge-from-trunk From hpk at codespeak.net Sun Feb 11 18:13:48 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 11 Feb 2007 18:13:48 +0100 (CET) Subject: [py-svn] r38486 - py/dist/py Message-ID: <20070211171348.48CA11009C@code0.codespeak.net> Author: hpk Date: Sun Feb 11 18:13:47 2007 New Revision: 38486 Added: py/dist/py/ - copied from r38485, py/trunk/py/ Log: use new py-trunk From hpk at codespeak.net Sun Feb 11 19:01:20 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 11 Feb 2007 19:01:20 +0100 (CET) Subject: [py-svn] r38496 - py/trunk/py/test/terminal Message-ID: <20070211180120.8D6661009A@code0.codespeak.net> Author: hpk Date: Sun Feb 11 19:01:19 2007 New Revision: 38496 Modified: py/trunk/py/test/terminal/terminal.py Log: don't print the test-mode, that needs more thought how to do it now Modified: py/trunk/py/test/terminal/terminal.py ============================================================================== --- py/trunk/py/test/terminal/terminal.py (original) +++ py/trunk/py/test/terminal/terminal.py Sun Feb 11 19:01:19 2007 @@ -125,8 +125,8 @@ # modes.insert(0, 'child process') #else: # modes.insert(0, 'inprocess') - mode = "/".join(modes) - self.out.line("testing-mode: %s" % mode) + #mode = "/".join(modes) + #self.out.line("testing-mode: %s" % mode) self.out.line("executable: %s (%s)" % (py.std.sys.executable, repr_pythonversion())) rev = py.__package__.getrev() From hpk at codespeak.net Sun Feb 11 19:36:40 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 11 Feb 2007 19:36:40 +0100 (CET) Subject: [py-svn] r38504 - in py/trunk/py/apigen: . tracer/testing Message-ID: <20070211183640.2682310087@code0.codespeak.net> Author: hpk Date: Sun Feb 11 19:36:38 2007 New Revision: 38504 Modified: py/trunk/py/apigen/htmlgen.py py/trunk/py/apigen/tracer/testing/test_package.py Log: fixes for python2.3 Modified: py/trunk/py/apigen/htmlgen.py ============================================================================== --- py/trunk/py/apigen/htmlgen.py (original) +++ py/trunk/py/apigen/htmlgen.py Sun Feb 11 19:36:38 2007 @@ -10,6 +10,8 @@ from py.__.apigen.linker import relpath from py.__.apigen.html import H +reversed = py.builtin.reversed + sorted = py.builtin.sorted html = py.xml.html raw = py.xml.raw @@ -423,7 +425,7 @@ if isinstance(val, property): val = '' properties.append((attr, val)) - properties.sort(key=lambda a: a[0]) # sort on name + properties.sort(lambda x,y : cmp(x[0], y[0])) # sort on name return properties def build_methods(self, dotted_name): Modified: py/trunk/py/apigen/tracer/testing/test_package.py ============================================================================== --- py/trunk/py/apigen/tracer/testing/test_package.py (original) +++ py/trunk/py/apigen/tracer/testing/test_package.py Sun Feb 11 19:36:38 2007 @@ -23,7 +23,7 @@ def test_init(self): ds = self.ds - print sorted(ds.descs.keys()) + print py.builtin.sorted(ds.descs.keys()) assert len(ds.descs) == 6 assert py.builtin.sorted(ds.descs.keys()) == [ 'notpak.notmod.notclass', 'notpak.notmod.notclass.__init__', From hpk at codespeak.net Sun Feb 11 19:43:35 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 11 Feb 2007 19:43:35 +0100 (CET) Subject: [py-svn] r38505 - py/trunk/py/test Message-ID: <20070211184335.E077F10081@code0.codespeak.net> Author: hpk Date: Sun Feb 11 19:43:34 2007 New Revision: 38505 Modified: py/trunk/py/test/config.py Log: clarify that config.getvalue will first lookup in options. Modified: py/trunk/py/test/config.py ============================================================================== --- py/trunk/py/test/config.py (original) +++ py/trunk/py/test/config.py Sun Feb 11 19:43:34 2007 @@ -120,8 +120,9 @@ return self.option def getvalue(self, name, path=None): - """ return 'name' value looked up from the first conftest file - found up the path (including the path itself). + """ return 'name' value looked up from the 'options' + and then from the first conftest file found up + the path (including the path itself). if path is None, lookup the value in the initial conftest modules found during command line parsing. """ From hpk at codespeak.net Sun Feb 11 19:47:05 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 11 Feb 2007 19:47:05 +0100 (CET) Subject: [py-svn] r38506 - in py/trunk/py/test: . testing Message-ID: <20070211184705.8209E10081@code0.codespeak.net> Author: hpk Date: Sun Feb 11 19:47:04 2007 New Revision: 38506 Modified: py/trunk/py/test/config.py py/trunk/py/test/testing/test_config.py Log: config.getvalue_pathlist should also honour option values (to be uniform with getvalue semantics) Modified: py/trunk/py/test/config.py ============================================================================== --- py/trunk/py/test/config.py (original) +++ py/trunk/py/test/config.py Sun Feb 11 19:47:04 2007 @@ -91,11 +91,14 @@ where they were found). """ try: - mod, relroots = self.conftest.rget_with_confmod(name, path) - except KeyError: - return None - modpath = py.path.local(mod.__file__).dirpath() - return [modpath.join(x, abs=True) for x in relroots] + return getattr(self.option, name) + except AttributeError: + try: + mod, relroots = self.conftest.rget_with_confmod(name, path) + except KeyError: + return None + modpath = py.path.local(mod.__file__).dirpath() + return [modpath.join(x, abs=True) for x in relroots] def addoptions(self, groupname, *specs): """ add a named group of options to the current testing session. Modified: py/trunk/py/test/testing/test_config.py ============================================================================== --- py/trunk/py/test/testing/test_config.py (original) +++ py/trunk/py/test/testing/test_config.py Sun Feb 11 19:47:04 2007 @@ -289,6 +289,10 @@ assert pl[0] == tmpdir assert pl[1] == somepath + config.option.mypathlist = [py.path.local()] + pl = config.getvalue_pathlist('mypathlist') + assert pl == [py.path.local()] + def test_config_iocapturing(self): self.tmpdir config = py.test.config._reparse([self.tmpdir]) From hpk at codespeak.net Sun Feb 11 19:56:09 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 11 Feb 2007 19:56:09 +0100 (CET) Subject: [py-svn] r38508 - py/trunk/py/test/rsession/testing Message-ID: <20070211185609.8655010081@code0.codespeak.net> Author: hpk Date: Sun Feb 11 19:56:08 2007 New Revision: 38508 Modified: py/trunk/py/test/rsession/testing/test_boxing.py Log: skip signal detection tests for python < 2.4 (2.4 and 2.5 work apparently) Modified: py/trunk/py/test/rsession/testing/test_boxing.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_boxing.py (original) +++ py/trunk/py/test/rsession/testing/test_boxing.py Sun Feb 11 19:56:08 2007 @@ -38,8 +38,10 @@ def test_boxing_signal(): b = Box(example2.boxseg, config=config) b.run() - assert b.signal == 11 assert b.retval is None + if py.std.sys.version_info < (2,4): + py.test.skip("signal detection does not work with python prior 2.4") + assert b.signal == 11 def test_boxing_huge_data(): b = Box(example2.boxhuge, config=config) @@ -88,4 +90,6 @@ par, pid = b.run(continuation=True) os.kill(pid, 15) par(pid) + if py.std.sys.version_info < (2,4): + py.test.skip("signal detection does not work with python prior 2.4") assert b.signal == 15 From hpk at codespeak.net Sun Feb 11 19:56:53 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 11 Feb 2007 19:56:53 +0100 (CET) Subject: [py-svn] r38509 - py/dist/py Message-ID: <20070211185653.86A1A10087@code0.codespeak.net> Author: hpk Date: Sun Feb 11 19:56:52 2007 New Revision: 38509 Removed: py/dist/py/ Log: remove for merge-from-trunk From hpk at codespeak.net Sun Feb 11 19:56:56 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 11 Feb 2007 19:56:56 +0100 (CET) Subject: [py-svn] r38510 - py/dist/py Message-ID: <20070211185656.1D4991009B@code0.codespeak.net> Author: hpk Date: Sun Feb 11 19:56:54 2007 New Revision: 38510 Added: py/dist/py/ - copied from r38509, py/trunk/py/ Log: use new py-trunk From hpk at codespeak.net Sun Feb 11 20:31:16 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 11 Feb 2007 20:31:16 +0100 (CET) Subject: [py-svn] r38514 - py/trunk/py/doc Message-ID: <20070211193116.347D410087@code0.codespeak.net> Author: hpk Date: Sun Feb 11 20:31:14 2007 New Revision: 38514 Modified: py/trunk/py/doc/why_py.txt Log: rewrite/refactor why_py (it was quite old) Modified: py/trunk/py/doc/why_py.txt ============================================================================== --- py/trunk/py/doc/why_py.txt (original) +++ py/trunk/py/doc/why_py.txt Sun Feb 11 20:31:14 2007 @@ -9,59 +9,22 @@ Why did we start the py lib? ============================ -.. _frustrations: +Among the main motivation for the py lib and its flagship +py.test tool were: -Frustrations and the fun of developing new ways ------------------------------------------------ +- to test applications with a testing tool that provides + advanced features out of the box, yet allows full customization + per-project. -Among the main motivations for writing the py lib is -frustration at existing python modules and packages, -among them: +- distribute applications in an ad-hoc way both for testing + and for application integration purposes. -- there is no standard way of testing python applications, - scripts or modules. everybody does its own hack around - the unittest.py if testing happens at all. +- help with neutralizing platform and python version differences -- due to python's default *export all names* policy for modules - and packages it is hard to refactor code across - releases because you might break usages of your code +- offer a uniform way to access local and remote file resources -- the python "batteries included" standard modules are tied to - the python release process. You can't easily benefit from - new python module in older python versions which you might - have to use for whatever reason. +- offer some unique features like micro-threads (greenlets) -- support for richer interactive interaction with command line - utilities or e.g. pygame-based shells is missing. - -- modules/packages are often implemented in javaesque -style - -- distributed applications are implemented using - a variant of Remote Method Invocation (RMI) which carries - a lot of problems and is often difficult to deploy - with respect to different platforms and versions. - -- there is no _automated_ way of installing, maintaining - and upgrading applications - -The py lib tries to address these problems. It is thus in -risk of trying to do too many things at once. Of course, we -can't solve them all at once but you will find that the above -points currently drive the development of the py lib. - - -Experimenting with new days, appreciating the existing ones ------------------------------------------------------------ - -First note that we very much appreciate the great work of all -the python developers and the python-dev team in particular. -Criticism of old ways and experimenting with new ways doesn't -imply that the old ways are bad. But we all strive for the -holy development grail at least some of the time, don't we? - -And because it happens far too rarely, let us especially emphasize -our appreciation for the `great work that Fred L. Drake, Jr. -is doing`_ with maintaining the core Python documentation. What is the py libs current focus? ================================== @@ -74,17 +37,24 @@ Writing, distributing and deploying tests should become a snap ... and fun! -Automated tests fit very well to the dynamism of Python. -Automated tests ease development and allow fast refactoring -cycles. Automated tests are a means of communication -as well. - -We try to allow test scripts with minimal boilerplate code or -no boilerplate at all. With the py lib you can simply use -``assert`` statements in order to - well - assert something -about objects in your code. No ``assertEqual(s)`` and all the -other kinds of funny names which only cover part of what you -want to assert about an expression, anyway. +On a side note: automated tests fit very well to the dynamism +of Python. Automated tests ease development and allow fast +refactoring cycles. Automated tests are a means of +communication as well. + + +ad-hoc distribution of programs +------------------------------------ + +The py lib through its `py.execnet`_ namespaces offers +support for ad-hoc distributing programs across +a network and subprocesses. We'd like to generalize +this approach further to instantiate and let whole +ad-hoc networks communicate with each other while +keeping to a simple programming model. + +.. _`py.execnet`: execnet.html + allowing maximum refactoring in the future ... ---------------------------------------------- @@ -92,42 +62,31 @@ explicit name export control ............................ -In order, to allow a fast development pace across versions of +In order to allow a fast development pace across versions of the py lib there is **explicit name export control**. You -will only see names which make sense to use from the outside -and which the py lib developers want to guarantee across multiple -revisions. However, you don't need to treat the ``py`` lib as +should only see names which make sense to use from the outside +and which the py lib developers want to guarantee across versions. +However, you don't need to treat the ``py`` lib as anything special. You can simply use the usual ``import`` statement and will not notice much of a difference - except that -the namespaces you'll see from the ``py`` lib will be extreamly +the namespaces you'll see from the ``py`` lib are relatively clean and have no clutter. -Indeed, much thought is given to reduce the exported *name -complexity*. This is an area where the python "batteries" and -many python packages unfortunately lack a lot. They expose so -many names that it becomes very hard to change APIs across -releases. People have been adding whole interface systems -because of that but arguably this adds another layer of names -instead of reducing the original problem. - -Anyway, exporting to many names kills the fun of refactoring -and improving things. We want to avoid that as much as -possible. - -Upcoming Release policy & API guarantees +Release policy & API maintenance ........................................ We'll talk about major, minor and micro numbers as the three -numbers in "1.2.3" respectively. These are the (draft!) -release policies: +numbers in "1.2.3" respectively. These are the +the rough release policies: -- Micro-releases are bug fix releases and may not introduce +- Micro-releases are bug fix releases and should not introduce new names to the public API. They may add tests and thus further define the behaviour of the py lib. They may completly change the implementation but the public API - tests will continue to run. + tests should continue to run (unless they needed to + get fixed themselves). -- No **tested feature** of the exported py API is to vanish +- No **tested feature** of the exported py API shall vanish across minor releases until it is marked deprecated. For example, pure API tests of a future version 1.0 are to @@ -144,14 +103,10 @@ a single leading '_' is generally seen as an implementation level detail. -- we intend to involve expert developers who give new APIs an - independent review before they go into a minor release - and even more so before they go into a major release. - - major releases *should*, but are not required to, pass all API tests of the previous latest major released - version. A full list of changes is to be included in - the release notes, including the tests that got abandoned. + version. + the need to find the right *paths* ... -------------------------------------- @@ -159,18 +114,12 @@ Another focus are well tested so called *path* implementations that allow you to seemlessly work with different backends, currently a local filesystem, subversion working copies and -subversion remote URLs. Moreover, there is an experimental -``extpy`` path to address a Python object on the (possibly -remote) filesystem. The `jorendorff path.py`_ implementation +subversion remote URLs. The `jorendorff path.py`_ implementation goes somewhat in the same direction but doesn't try to systematically access local and remote file systems as well as other hierarchic namespaces. The latter is the focus of the ``py.path`` API. -If you are ready to grasp more then you may try reading -about future_ coding goals of the py lib, which reflect the -current developers thoughts and discussions. - .. _`jorendorff path.py`: http://www.jorendorff.com/articles/python/path/ How does py development work? @@ -189,76 +138,42 @@ experienced python developer and share some of the frustrations described above. -Moreover, the world will be granted svn commit rights to all -py test files so that you can easily add bug-tests or tests -for behaviour to make sure the tested behaviour persists -across releases. If you are itching to actually fix or -refactor any implementation code you can likely get commit -rights to do it. However, it is then (and anyway) a good idea to -follow the `py-dev mailing list`_ and grasp some of the things -that are going on in the `future`_ book. - -Licensing, please +Licensing ----------------- -Oh right, and you should also agree to release your contributions -under an ``MIT license`` and consequently any other OSI-approved -license. This is FOSS [#]_ and we want to have the py lib interopable -with whatever license style you prefer. Copyright generally -stays with the contributors. We are following the -linux-kernel dev-model with this one. - -Hum, maybe we can also compute the individual copyrights from -the subversion blame command. Would be a fun way to handle it. -Basically, nobody should ever have a problem to use the py lib -under any OSI approved license and also for commercial -purposes. +The Py lib is released under the MIT license and all +contributors need to release their contributions +under this license as well. -Are there connections with PyPy_? +connections with PyPy_ --------------------------------- -Some of the motivation for writing the py lib stems from needs +A major motivation for writing the py lib stems from needs during PyPy_ development, most importantly testing and file system access issues. PyPy puts a lot of pressure -on a testing environment and thus is a great **reality test** -kind of thing. - -More importantly, the development perspective taken from the -PyPy developers has some influence. For example, the -`envisioned import/export system`_ clearly has some thought -references to the way PyPy tries to separate different levels -of objects and execution in order to reach a higher level -view of what is going on. +on a testing environment and thus is a good **reality test**. -Who is "we"? Some history ... +Who is "we"? ============================= Some initial code was written from *Jens-Uwe Mager* and *Holger Krekel*, after which Holger continued on a previous -incarnation of the py.test tool (known first as 'utest', then -as 'std.utest', now, finally and at last 'py.test'). +incarnations of the py.test tool (known first as 'utest', then +as 'std.utest', now for some 2 years 'py.test'). Helpful discussions took place with *Martijn Faassen*, *Stephan -Schwarzer* and then *Armin Rigo* who contributed important parts. +Schwarzer*, *Brian Dorsey*, *Grigh Gheorghiu* and then +*Armin Rigo* who contributed important parts. He and Holger came up with a couple of iterations of the -testing-code that reduced the API to almost nothing: just the +testing-code that reduced the API to basically nothing: just the plain assert statement and a ``py.test.raises`` method to -check for occuring exceptions. +check for occuring exceptions within tests. -Now recently, after Holgers `talk at EP2004`_ more people -were interested and there were discussions with *Jim Fulton*, -*Marius Gedminas*, *Laura Creighton* and more recently, *Ian Bicking*. - -However, there is no real core development team as such, yet. -Also we are somewhat lacking in the win32 area. Every now and -then the py lib is tested on windows but it's currently not a -practical concern of one of the current developers or -contributors. - -However, one of the future directions of the `py.test tool and -library`_ is to allow running tests across multiple python -versions and computers. Then we can run tests without having -to walk up or boot up a windows machine :-) +Currently (as of 2007), there are more people involved +and also have worked funded through merlinux_ and the +PyPy EU project, Carl Friedrich Bolz, Guido Wesdorp +and Maciej Fijalkowski who contributed particularly +in 2006 and 2007 major parts of the py lib. .. _`talk at EP2004`: http://codespeak.net/svn/user/hpk/talks/std-talk.txt .. _`coding style`: coding-style.html @@ -270,6 +185,7 @@ .. _future: future/future.html .. _`py.test tool and library`: test.html .. _`great work that Fred L. Drake, Jr. is doing`: http://www.python.org/doc/2.3.4/lib/lib.html +.. _merlinux: http://merlinux.de -- From hpk at codespeak.net Sun Feb 11 20:52:12 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 11 Feb 2007 20:52:12 +0100 (CET) Subject: [py-svn] r38516 - py/trunk/py/doc/future Message-ID: <20070211195212.AD8DA10089@code0.codespeak.net> Author: hpk Date: Sun Feb 11 20:52:11 2007 New Revision: 38516 Modified: py/trunk/py/doc/future/future.txt Log: majorly refactor future chapter, mentioning APIgen and other more current ideas Modified: py/trunk/py/doc/future/future.txt ============================================================================== --- py/trunk/py/doc/future/future.txt (original) +++ py/trunk/py/doc/future/future.txt Sun Feb 11 20:52:11 2007 @@ -9,321 +9,62 @@ for the near-future development of the py lib. *Note that all statements within this document - even if they sound factual - mostly just express thoughts and ideas. They not always refer to -real code so read with some caution. This is not a reference guide -(tm). Moreover, the order in which appear here in the file does -not reflect the order in which they may be implemented.* +real code so read with some caution.* .. _`general-path`: .. _`a more general view on path objects`: -A more general view on ``py.path`` objects -========================================== -Seen from a more general persective, the current ``py.path.extpy`` path -offers a way to go from a file to the structured content of -a file, namely a python object. The ``extpy`` path retains some -common ``path`` operations and semantics but offers additional -methods, e.g. ``resolve()`` gets you a true python object. - -But apart from python files there are many other examples -of structured content like xml documents or INI-style -config files. While some tasks will only be convenient -to perform in a domain specific manner (e.g. applying xslt -etc.pp) ``py.path`` offers a common behaviour for -structured content paths. So far only ``py.path.extpy`` -is implemented and used by py.test to address tests -and traverse into test files. - -*You are in a maze of twisty passages, all alike* -------------------------------------------------- - -Now, for the sake of finding out a good direction, -let's consider some code that wants to find all -*sections* which have a certain *option* value -within some given ``startpath``:: - - def find_option(startpath, optionname): - for section in startpath.listdir(dir=1): - opt = section.join(optionname) - if opt.check(): # does the option exist here? - print section.basename, "found:", opt.read() - -Now the point is that ``find_option()`` would obviously work -when ``startpath`` is a filesystem-like path like a local -filesystem path or a subversion URL path. It would then see -directories as sections and files as option-names and the -content of the file as values. - -But it also works (today) for ``extpy`` paths if you put the following -python code in a file:: - - class Section1: - someoption = "i am an option value" - - class Section2: - someoption = "i am another option value" - -An ``extpy()`` path maps classes and modules to directories and -name-value bindings to file/read() operations. - -And it could also work for 'xml' paths if you put -the following xml string in a file:: - - - - - value - - value - -where tags containing non-text tags map to directories -and tags with just text-children map to files (which -upon read() return the joined content of the text -tags possibly as unicode. - -Now, to complete the picture, we could make Config-Parser -*ini-style* config files also available:: - - [section1] - name = value - - [section2] - othername = value - -where sections map to directories and name=value mappings -to file/contents. - -So it seems that our above ``find_option()`` function would -work nicely on all these *mappings*. - -Of course, the somewhat open question is how to make the -transition from a filesystem path to structured content -useful and unified, as much as possible without overdoing it. - -Again, there are tasks that will need fully domain specific -solutions (DOM/XSLT/...) but i think the above view warrants -some experiments and refactoring. The degree of uniformity -still needs to be determined and thought about. - -path objects should be stackable --------------------------------- - -Oh, and btw, a ``py.path.extpy`` file could live on top of a -'py.path.xml' path as well, i.e. take:: - - - - - - - import py - ... - - def getmsg(x): pass - -and use it to have a ``extpy`` path living on it:: - - p = py.path.local(xmlfilename) - xmlp = py.path.extxml(p, 'py/magic/exprinfo') - p = py.path.extpy(xmlp, 'getmsg') - - assert p.check(func=1, basename='getmsg') - getmsg = p.resolve() - # we now have a *live* getmsg() function taken and compiled from - # the above xml fragment - -There could be generic converters which convert between -different content formats ... allowing configuration files to e.g. -be in XML/Ini/python or filesystem-format with some common way -to find and iterate values. - -*After all the unix filesystem and the python namespaces are -two honking great ideas, why not do more of them? :-)* - - -.. _importexport: - -Revising and improving the import/export system -=============================================== - - or let's wrap the world all around - -the export/import interface ---------------------------- - -The py lib already incorporates a mechanism to select which -namespaces and names get exposed to a user of the library. -Apart from reducing the outside visible namespaces complexity -this allows to quickly rename and refactor stuff in the -implementation without affecting the caller side. This export -control can be used by other python packages as well. - -However, all is not fine as the import/export has a -few major deficiencies and shortcomings: - -- it doesn't allow to specify doc-strings -- it is a bit hackish (see py/initpkg.py) -- it doesn't present a complete and consistent view of the API. -- ``help(constructed_namespace)`` doesn't work for the root - package namespace -- when the py lib implementation accesses parts of itself - it uses the native python import mechanism which is - limiting in some respects. Especially for distributed - programs as encouraged by `py.execnet`_ it is not clear - how the mechanism can nicely integrate to support remote - lazy importing. - -Discussions have been going on for a while but it is -still not clear how to best tackle the problem. Personally, -i believe the main missing thing for the first major release -is the docstring one. The current specification -of exported names is dictionary based. It would be -better to declare it in terms of Objects. - - -Example sketch for a new export specification ---------------------------------------------- - -Here is a sketch of how the py libs ``__init__.py`` file -might or should look like:: - - """ - the py lib version 1.0 - http://codespeak.net/py/1.0 - """ - - from py import pkg - pkg.export(__name__, - pkg.Module('path', - '''provides path objects for local filesystem, - subversion url and working copy, and extension paths. - ''', - pkg.Class('local', ''' - the local filesystem path offering a single - point of interaction for many purposes. - ''', extpy='./path/local.LocalPath'), - - pkg.Class('svnurl', ''' - the subversion url path. - ''', extpy='./path/local/svn/urlcommand.SvnUrlPath'), - ), - # it goes on ... - ) - -The current ``initpkg.py`` code can be cleaned up to support -this new more explicit style of stating things. Note that -in principle there is nothing that stops us from retrieving -implementations over the network, e.g. a subversion repository. - - -Let there be alternatives -------------------------- - -We could also specify alternative implementations easily:: - - pkg.Class('svnwc', ''' - the subversion working copy. - ''', extpy=('./path/local/svn/urlbinding.SvnUrlPath', - './path/local/svn/urlcommand.SvnUrlPath',) - ) - -This would prefer the python binding based implementation over -the one working through he 'svn' command line utility. And -of course, it could uniformly signal if no implementation is -available at all. - - -Problems problems ------------------ - -Now there are reasons there isn't a clear conclusion so far. -For example, the above approach has some implications, the -main one being that implementation classes like -``py/path/local.LocalPath`` are visible to the caller side but -this presents an inconsistency because the user started out with -``py.path.local`` and expects that the two classes are really much -the same. We have the same problem today, of course. - -The naive solution strategy of wrapping the "implementation -level" objects into their exported representations may remind -of the `wrapping techniques PyPy uses`_. But it -*may* result in a slightly heavyweight mechanism that affects -runtime speed. However, I guess that this standard strategy -is probably the cleanest. - - -Every problem can be solved with another level ... --------------------------------------------------- - -The wrapping of implementation level classes in their export -representations objects adds another level of indirection. -But this indirection would have interesting advantages: - -- we could easily present a consistent view of the library -- it could take care of exceptions as well -- it provides natural interception points for logging -- it enables remote lazy loading of implementations - or certain versions of interfaces - -And quite likely the extra indirection wouldn't hurt so much -as it is not much more than a function call and we cared -we could even generate some c-code (with PyPy :-) to speed -it up. - -But it can lead to new problems ... ------------------------------------ - -However, it is critical to avoid to burden the implementation -code of being aware of its wrapping. This is what we have -to do in PyPy but the import/export mechanism works at -a higher level of the language, i think. - -Oh, and we didn't talk about bootstrapping :-) - -.. _`py.execnet`: ../execnet.html -.. _`wrapping techniques PyPy uses`: http://codespeak.net/pypy/index.cgi?doc/wrapping.html -.. _`lightweight xml generation`: - -Extension of py.path.local.sysexec() -==================================== - -The `sysexec mechanism`_ allows to directly execute -binaries on your system. Especially after we'll have this -nicely integrated into Win32 we may also want to run python -scripts both locally and from the net:: - - vadm = py.path.svnurl('http://codespeak.net/svn/vadm/dist/vadm/cmdline.py') - stdoutput = vadm.execute('diff') - -To be able to execute this code fragement, we need either or all of - -- an improved import system that allows remote imports - -- a way to specify what the "neccessary" python import - directories are. for example, the above scriptlet will - require a certain root included in the python search for module - in order to execute something like "import vadm". - -- a way to specify dependencies ... which opens up another - interesting can of worms, suitable for another chapter - in the neverending `future book`_. - -.. _`sysexec mechanism`: ../misc.html#sysexec -.. _`compile-on-the-fly`: - -we need a persistent storage for the py lib -------------------------------------------- - -A somewhat open question is where to store the underlying -generated pyc-files and other files generated on the fly -with `CPython's distutils`_. We want to have a -*persistent location* in order to avoid runtime-penalties -when switching python versions and platforms (think NFS). - -A *persistent location* for the py lib would be a good idea -maybe also for other reasons. We could cache some of the -expensive test setups, like the multi-revision subversion -repository that is created for each run of the tests. +Distribute tests ad-hoc across multiple platforms +====================================================== + +After some more refactoring and unification of +the current testing and distribution support code +we'd like to be able to run tests on multiple +platforms simultanously and allow for interaction +and introspection into the (remote) failures. + + +Make APIGEN useful for more projects +================================================ + +The new APIGEN tool offers rich information +derived from running tests against an application: +argument types and callsites, i.e. it shows +the places where a particular API is used. +In its first incarnation, there are still +some specialties that likely prevent it +from documenting APIs for other projects. +We'd like to evolve to a `py.apigen` tool +that can make use of information provided +by a py.test run. + +Distribute channels/programs across networks +================================================ + +Apart from stabilizing setup/teardown procedures +for `py.execnet`_, we'd like to generalize its +implementation to allow connecting two programs +across multiple hosts, i.e. we'd like to arbitrarily +send "channels" across the network. Likely this +will be done by using the "pipe" model, i.e. +that each channel is actually a pair of endpoints, +both of which can be independently transported +across the network. The programs who "own" +these endpoints remain connected. + +.. _`py.execnet`: ../execnet.html + +Benchmarking and persistent storage +========================================= + +For storing test results, but also benchmarking +and other information, we need a solid way +to store all kinds of information from test runs. +We'd like to generate statistics or html-overview +out of it, but also use such information to determine when +a certain test broke, or when its performance +decreased considerably. .. _`CPython's distutils`: http://www.python.org/dev/doc/devel/lib/module-distutils.html @@ -364,59 +105,12 @@ .. _`reiserfs v4 features`: http://www.namesys.com/v4/v4.html -Improve and unify Path API -========================== - -visit() grows depth control ---------------------------- - -Add a ``maxdepth`` argument to the path.visit() method, -which will limit traversal to subdirectories. Example:: - x = py.path.local.get_tmproot() - for x in p.visit('bin', stop=N): - ... - -This will yield all file or directory paths whose basename -is 'bin', depending on the values of ``stop``:: - - p # stop == 0 or higher (and p.basename == 'bin') - p / bin # stop == 1 or higher - p / ... / bin # stop == 2 or higher - p / ... / ... / bin # stop == 3 or higher - -The default for stop would be `255`. - -But what if `stop < 0`? We could let that mean to go upwards:: - - for x in x.visit('py/bin', stop=-255): - # will yield all parent direcotires which have a - # py/bin subpath - -visit() returning a lazy list? ------------------------------- - -There is a very nice "no-API" `lazy list`_ implementation from -Armin Rigo which presents a complete list interface, given some -iterable. The iterable is consumed only on demand and retains -memory efficiency as much as possible. The lazy list -provides a number of advantages in addition to the fact that -a list interface is nicer to deal with than an iterator. -For example it lets you do:: - - for x in p1.visit('*.cfg') + p2.visit('*.cfg'): - # will iterate through all results - -Here the for-iter expression will retain all lazyness (with -the result of adding lazy lists being another another lazy -list) by internally concatenating the underlying -lazylists/iterators. Moreover, the lazylist implementation -will know that there are no references left to the lazy list -and throw away iterated elements. This makes the iteration -over the sum of the two visit()s as efficient as if we had -used iterables to begin with! +Consider more features +================================== -For this, we would like to move the lazy list into the -py lib's namespace, most probably at `py.builtin.lazylist`. +There are many more features and useful classes +that might be nice to integrate. For example, we might put +Armin's `lazy list`_ implementation into the py lib. .. _`lazy list`: http://codespeak.net/svn/user/arigo/hack/misc/collect.py From hpk at codespeak.net Sun Feb 11 20:57:13 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 11 Feb 2007 20:57:13 +0100 (CET) Subject: [py-svn] r38518 - py/trunk/py/doc Message-ID: <20070211195713.623981008D@code0.codespeak.net> Author: hpk Date: Sun Feb 11 20:57:12 2007 New Revision: 38518 Modified: py/trunk/py/doc/misc.txt py/trunk/py/doc/why_py.txt Log: fix references Modified: py/trunk/py/doc/misc.txt ============================================================================== --- py/trunk/py/doc/misc.txt (original) +++ py/trunk/py/doc/misc.txt Sun Feb 11 20:57:12 2007 @@ -142,7 +142,6 @@ will have to install the `pywin32 package`_. -.. _`compile-on-the-fly`: future/future.html#compile-on-the-fly .. _`future book`: future/future.html .. _`PEP-324 subprocess module`: http://www.python.org/peps/pep-0324.html .. _`subprocess implementation`: http://www.lysator.liu.se/~astrand/popen5/ Modified: py/trunk/py/doc/why_py.txt ============================================================================== --- py/trunk/py/doc/why_py.txt (original) +++ py/trunk/py/doc/why_py.txt Sun Feb 11 20:57:12 2007 @@ -181,10 +181,8 @@ .. _`py-dev mailing list`: http://codespeak.net/mailman/listinfo/py-dev .. _`test environment`: test.html .. _`PyPy`: http://codespeak.net/pypy -.. _`envisioned import/export system`: future/future.html#importexport .. _future: future/future.html .. _`py.test tool and library`: test.html -.. _`great work that Fred L. Drake, Jr. is doing`: http://www.python.org/doc/2.3.4/lib/lib.html .. _merlinux: http://merlinux.de -- From hpk at codespeak.net Sun Feb 11 21:03:23 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 11 Feb 2007 21:03:23 +0100 (CET) Subject: [py-svn] r38519 - py/dist/py Message-ID: <20070211200323.DEA7F1009A@code0.codespeak.net> Author: hpk Date: Sun Feb 11 21:03:21 2007 New Revision: 38519 Removed: py/dist/py/ Log: remove for merge-from-trunk From hpk at codespeak.net Sun Feb 11 21:03:25 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Sun, 11 Feb 2007 21:03:25 +0100 (CET) Subject: [py-svn] r38520 - py/dist/py Message-ID: <20070211200325.7352F1009A@code0.codespeak.net> Author: hpk Date: Sun Feb 11 21:03:24 2007 New Revision: 38520 Added: py/dist/py/ - copied from r38519, py/trunk/py/ Log: use new py-trunk From cfbolz at codespeak.net Sun Feb 11 23:16:23 2007 From: cfbolz at codespeak.net (cfbolz at codespeak.net) Date: Sun, 11 Feb 2007 23:16:23 +0100 (CET) Subject: [py-svn] r38524 - py/trunk/py/doc/future Message-ID: <20070211221623.28EA410093@code0.codespeak.net> Author: cfbolz Date: Sun Feb 11 23:16:23 2007 New Revision: 38524 Modified: py/trunk/py/doc/future/future.txt Log: add link to pyfuse (which is the newer approach, I think) and fix the link target. Modified: py/trunk/py/doc/future/future.txt ============================================================================== --- py/trunk/py/doc/future/future.txt (original) +++ py/trunk/py/doc/future/future.txt Sun Feb 11 23:16:23 2007 @@ -11,9 +11,6 @@ mostly just express thoughts and ideas. They not always refer to real code so read with some caution.* -.. _`general-path`: -.. _`a more general view on path objects`: - Distribute tests ad-hoc across multiple platforms ====================================================== @@ -77,6 +74,10 @@ .. _`subprocess implementation`: http://www.lysator.liu.se/~astrand/popen5/ .. _`py.test`: ../test.html + +.. _`general-path`: +.. _`a more general view on path objects`: + Refactor path implementations to use a Filesystem Abstraction ============================================================= @@ -86,7 +87,8 @@ local, subversion and subversion "working copy" filesystems. Today the according code is scattered through path-handling code. -On a related note, Armin Rigo has hacked `pylufs`_ which allows to +On a related note, Armin Rigo has hacked `pylufs`_ and more recently has +written `pyfuse`_ which allows to implement kernel-level linux filesystems with pure python. Now the idea is that the mentioned filesystem implementations would be directly usable for such linux-filesystem glue code. @@ -102,6 +104,7 @@ .. _`memoryfs`: http://codespeak.net/svn/user/arigo/hack/pyfuse/memoryfs.py .. _`dictfs`: http://codespeak.net/pipermail/py-dev/2005-January/000191.html .. _`pylufs`: http://codespeak.net/svn/user/arigo/hack/pylufs/ +.. _`pyfuse`: http://codespeak.net/svn/user/arigo/hack/pyfuse/ .. _`reiserfs v4 features`: http://www.namesys.com/v4/v4.html From hpk at codespeak.net Mon Feb 12 01:09:58 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 12 Feb 2007 01:09:58 +0100 (CET) Subject: [py-svn] r38530 - py/trunk/py/doc Message-ID: <20070212000958.569AA1009A@code0.codespeak.net> Author: hpk Date: Mon Feb 12 01:09:57 2007 New Revision: 38530 Modified: py/trunk/py/doc/index.txt Log: fix Modified: py/trunk/py/doc/index.txt ============================================================================== --- py/trunk/py/doc/index.txt (original) +++ py/trunk/py/doc/index.txt Mon Feb 12 01:09:57 2007 @@ -38,7 +38,7 @@ `why what how py?`_, describing motivation and background of the py lib -.. _`download and installation`: download.txt +.. _`download and installation`: download.html .. _`py-dev at codespeak net`: http://codespeak.net/mailman/listinfo/py-dev .. _`py.execnet`: execnet.html .. _`py.magic.greenlet`: greenlet.html From hpk at codespeak.net Mon Feb 12 01:29:29 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 12 Feb 2007 01:29:29 +0100 (CET) Subject: [py-svn] r38531 - py/dist/py Message-ID: <20070212002929.EC3CF10077@code0.codespeak.net> Author: hpk Date: Mon Feb 12 01:29:28 2007 New Revision: 38531 Removed: py/dist/py/ Log: remove for merge-from-trunk From hpk at codespeak.net Mon Feb 12 01:29:31 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 12 Feb 2007 01:29:31 +0100 (CET) Subject: [py-svn] r38532 - py/dist/py Message-ID: <20070212002931.37E7B10077@code0.codespeak.net> Author: hpk Date: Mon Feb 12 01:29:30 2007 New Revision: 38532 Added: py/dist/py/ - copied from r38531, py/trunk/py/ Log: use new py-trunk From hpk at codespeak.net Mon Feb 12 01:37:49 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 12 Feb 2007 01:37:49 +0100 (CET) Subject: [py-svn] r38533 - py/trunk/py/doc Message-ID: <20070212003749.6E91F10077@code0.codespeak.net> Author: hpk Date: Mon Feb 12 01:37:48 2007 New Revision: 38533 Modified: py/trunk/py/doc/confrest.py py/trunk/py/doc/conftest.py py/trunk/py/doc/test_conftest.py Log: adding a way to modify the "apigen relative path" from the command line, unifying conftest and confrest Modified: py/trunk/py/doc/confrest.py ============================================================================== --- py/trunk/py/doc/confrest.py (original) +++ py/trunk/py/doc/confrest.py Mon Feb 12 01:37:48 2007 @@ -1,6 +1,7 @@ import py from py.__.misc.rest import convert_rest_html, strip_html_header from py.__.misc.difftime import worded_time +from py.__.doc.conftest import get_apigen_relpath mydir = py.magic.autopath().dirpath() html = py.xml.html @@ -22,6 +23,7 @@ self.fill() def fill(self): + apigen_relpath = get_apigen_relpath() content_type = "%s;charset=%s" %(self.type, self.encoding) self.head.append(html.title(self.title)) self.head.append(html.meta(name="Content-Type", content=content_type)) @@ -33,12 +35,12 @@ self.menubar = html.div( html.a("home", href="home.html", class_="menu"), " ", html.a("doc", href="index.html", class_="menu"), " ", - html.a("api", href="../../apigen/api/index.html", class_="menu"), + html.a("api", href=apigen_relpath + "api/index.html", class_="menu"), " ", - html.a("source", href="../../apigen/source/index.html", + html.a("source", href=apigen_relpath + "source/index.html", class_="menu"), " ", html.a("contact", href="contact.html", class_="menu"), " ", - html.a("getting-started", href="getting-started.html", class_="menu"), " ", + html.a("download", href="download.html", class_="menu"), " ", id="menubar", ) self.metaspace = html.div( Modified: py/trunk/py/doc/conftest.py ============================================================================== --- py/trunk/py/doc/conftest.py (original) +++ py/trunk/py/doc/conftest.py Mon Feb 12 01:37:48 2007 @@ -11,9 +11,17 @@ Option('', '--forcegen', action="store_true", dest="forcegen", default=False, help="force generation of html files even if they appear up-to-date" + ), + Option('', '--apigenrelpath', + action="store", dest="apigen_relpath", default="../../apigen", + type="string", + help="force generation of html files even if they appear up-to-date" ) ) +def get_apigen_relpath(): + return py.test.config.option.apigen_relpath + "/" + def deindent(s, sep='\n'): leastspaces = -1 lines = s.split(sep) @@ -254,9 +262,10 @@ Directory = DocDirectory def resolve_linkrole(name, text, check=True): + apigen_relpath = get_apigen_relpath() if name == 'api': if text == 'py': - return ('py', '../../apigen/api/index.html') + return ('py', apigen_relpath + 'api/index.html') else: assert text.startswith('py.'), ( 'api link "%s" does not point to the py package') % (text,) @@ -275,7 +284,7 @@ raise AssertionError( 'problem with linkrole :api:`%s`: can not resolve ' 'dotted name %s' % (text, dotted_name,)) - return (text, '../../apigen/api/%s.html' % (dotted_name,)) + return (text, apigen_relpath + 'api/%s.html' % (dotted_name,)) elif name == 'source': assert text.startswith('py/'), ('source link "%s" does not point ' 'to the py package') % (text,) @@ -290,5 +299,5 @@ relpath += 'index.html' else: relpath += '.html' - return (text, '../../apigen/source/%s' % (relpath,)) + return (text, apigen_relpath + 'source/%s' % (relpath,)) Modified: py/trunk/py/doc/test_conftest.py ============================================================================== --- py/trunk/py/doc/test_conftest.py (original) +++ py/trunk/py/doc/test_conftest.py Mon Feb 12 01:37:48 2007 @@ -110,20 +110,22 @@ assert len(l+l2) == 3 def test_resolve_linkrole(): + from py.__.doc.conftest import get_apigen_relpath + apigen_relpath = get_apigen_relpath() from py.__.doc.conftest import resolve_linkrole assert resolve_linkrole('api', 'py.foo.bar', False) == ( - 'py.foo.bar', '../../apigen/api/foo.bar.html') + 'py.foo.bar', apigen_relpath + 'api/foo.bar.html') assert resolve_linkrole('api', 'py.foo.bar()', False) == ( - 'py.foo.bar()', '../../apigen/api/foo.bar.html') + 'py.foo.bar()', apigen_relpath + 'api/foo.bar.html') assert resolve_linkrole('api', 'py', False) == ( - 'py', '../../apigen/api/index.html') + 'py', apigen_relpath + 'api/index.html') py.test.raises(AssertionError, 'resolve_linkrole("api", "foo.bar")') assert resolve_linkrole('source', 'py/foo/bar.py', False) == ( - 'py/foo/bar.py', '../../apigen/source/foo/bar.py.html') + 'py/foo/bar.py', apigen_relpath + 'source/foo/bar.py.html') assert resolve_linkrole('source', 'py/foo/', False) == ( - 'py/foo/', '../../apigen/source/foo/index.html') + 'py/foo/', apigen_relpath + 'source/foo/index.html') assert resolve_linkrole('source', 'py/', False) == ( - 'py/', '../../apigen/source/index.html') + 'py/', apigen_relpath + 'source/index.html') py.test.raises(AssertionError, 'resolve_linkrole("source", "/foo/bar/")') def test_resolve_linkrole_check_api(): From hpk at codespeak.net Mon Feb 12 01:41:07 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 12 Feb 2007 01:41:07 +0100 (CET) Subject: [py-svn] r38534 - py/trunk/py/apigen Message-ID: <20070212004107.05B38100A2@code0.codespeak.net> Author: hpk Date: Mon Feb 12 01:41:07 2007 New Revision: 38534 Modified: py/trunk/py/apigen/todo.txt Log: some more little issues Modified: py/trunk/py/apigen/todo.txt ============================================================================== --- py/trunk/py/apigen/todo.txt (original) +++ py/trunk/py/apigen/todo.txt Mon Feb 12 01:41:07 2007 @@ -1,11 +1,16 @@ -* get konqueror to display indents in source code better? - (currently it doesn't look like more than a single space) - * source page headers should read: py/apigen sources [rev XXX] +* and "namespace" pages: + + builtin namespace index [rev XXX] + +* get konqueror to display indents in source code better? + (currently it doesn't look like more than a single space) + + * function view: def __init__(self, rawcode): From guido at codespeak.net Mon Feb 12 12:16:19 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Mon, 12 Feb 2007 12:16:19 +0100 (CET) Subject: [py-svn] r38542 - py/trunk/py/doc Message-ID: <20070212111619.7E56F100A7@code0.codespeak.net> Author: guido Date: Mon Feb 12 12:16:18 2007 New Revision: 38542 Modified: py/trunk/py/doc/conftest.py Log: Fixed help string for --apigenrelpath. Modified: py/trunk/py/doc/conftest.py ============================================================================== --- py/trunk/py/doc/conftest.py (original) +++ py/trunk/py/doc/conftest.py Mon Feb 12 12:16:18 2007 @@ -15,7 +15,8 @@ Option('', '--apigenrelpath', action="store", dest="apigen_relpath", default="../../apigen", type="string", - help="force generation of html files even if they appear up-to-date" + help=("specify the relative path to apigen (used for link " + "generation)") ) ) From guido at codespeak.net Mon Feb 12 12:36:08 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Mon, 12 Feb 2007 12:36:08 +0100 (CET) Subject: [py-svn] r38546 - py/trunk/py/doc/future Message-ID: <20070212113608.539A7100A3@code0.codespeak.net> Author: guido Date: Mon Feb 12 12:36:07 2007 New Revision: 38546 Modified: py/trunk/py/doc/future/future.txt Log: Typo. Modified: py/trunk/py/doc/future/future.txt ============================================================================== --- py/trunk/py/doc/future/future.txt (original) +++ py/trunk/py/doc/future/future.txt Mon Feb 12 12:36:07 2007 @@ -88,7 +88,7 @@ the according code is scattered through path-handling code. On a related note, Armin Rigo has hacked `pylufs`_ and more recently has -written `pyfuse`_ which allows to +written `pyfuse`_ which allow to implement kernel-level linux filesystems with pure python. Now the idea is that the mentioned filesystem implementations would be directly usable for such linux-filesystem glue code. From hpk at codespeak.net Mon Feb 12 13:18:25 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 12 Feb 2007 13:18:25 +0100 (CET) Subject: [py-svn] r38548 - in py/trunk/py/test: . rsession terminal testing Message-ID: <20070212121825.BC855100A7@code0.codespeak.net> Author: hpk Date: Mon Feb 12 13:18:23 2007 New Revision: 38548 Modified: py/trunk/py/test/config.py py/trunk/py/test/rsession/slave.py py/trunk/py/test/terminal/remote.py py/trunk/py/test/testing/test_config.py Log: carefully privatizing Config.make_repr and Config.merge_repr by doing a whole-pylib replace and reviewing the diff in detail Modified: py/trunk/py/test/config.py ============================================================================== --- py/trunk/py/test/config.py (original) +++ py/trunk/py/test/config.py Mon Feb 12 13:18:23 2007 @@ -56,7 +56,7 @@ assert not self._initialized self._initialized = True self.topdir = py.path.local(topdir) - self.merge_repr(repr) + self._mergerepr(repr) self._coltrails = coltrails def getcolitems(self): @@ -189,7 +189,7 @@ finally: config_per_process = py.test.config = oldconfig - def make_repr(self, conftestnames, optnames=None): + def _makerepr(self, conftestnames, optnames=None): """ return a marshallable representation of conftest and cmdline options. if optnames is None, all options @@ -214,10 +214,10 @@ l.append(path.relto(self.topdir)) return l, conftestdict, cmdlineopts - def merge_repr(self, repr): + def _mergerepr(self, repr): """ merge in the conftest and cmdline option values found in the given representation (produced - by make_repr above). + by _makerepr above). The repr-contained conftest values are stored on the default conftest module (last Modified: py/trunk/py/test/rsession/slave.py ============================================================================== --- py/trunk/py/test/rsession/slave.py (original) +++ py/trunk/py/test/rsession/slave.py Mon Feb 12 13:18:23 2007 @@ -70,7 +70,7 @@ defaultconftestnames = ['dist_nicelevel'] def setup_slave(host, config): channel = host.gw.remote_exec(str(py.code.Source(setup, "setup()"))) - configrepr = config.make_repr(defaultconftestnames) + configrepr = config._makerepr(defaultconftestnames) #print "sending configrepr", configrepr topdir = host.gw_remotepath if topdir is None: Modified: py/trunk/py/test/terminal/remote.py ============================================================================== --- py/trunk/py/test/terminal/remote.py (original) +++ py/trunk/py/test/terminal/remote.py Mon Feb 12 13:18:23 2007 @@ -104,7 +104,7 @@ """, stdout=self.out, stderr=self.out) try: print "MASTER: initiated slave terminal session ->" - repr = self.config.make_repr(conftestnames=[]) + repr = self.config._makerepr(conftestnames=[]) channel.send((str(topdir), repr, failures)) print "MASTER: send start info, topdir=%s" % (topdir,) try: Modified: py/trunk/py/test/testing/test_config.py ============================================================================== --- py/trunk/py/test/testing/test_config.py (original) +++ py/trunk/py/test/testing/test_config.py Mon Feb 12 13:18:23 2007 @@ -128,7 +128,7 @@ tmp.ensure("conftest.py").write("x=1 ; y=2") hello = tmp.ensure("test_hello.py") config = py.test.config._reparse([hello]) - repr = config.make_repr(conftestnames=['x', 'y']) + repr = config._makerepr(conftestnames=['x', 'y']) config2 = py.test.config._reparse([tmp.dirpath()]) config2._initialized = False # we have to do that from tests config2.initdirect(topdir=tmp.dirpath(), repr=repr) @@ -148,19 +148,19 @@ assert col.parent.name == tmp.basename assert col.parent.parent is None -def test_config_make_and_merge_repr(): +def test_config_make_and__mergerepr(): tmp = py.test.ensuretemp("reprconfig1") tmp.ensure("__init__.py") tmp.ensure("conftest.py").write("x=1") config = py.test.config._reparse([tmp]) - repr = config.make_repr(conftestnames=['x']) + repr = config._makerepr(conftestnames=['x']) config.option.verbose = 42 - repr2 = config.make_repr(conftestnames=[], optnames=['verbose']) + repr2 = config._makerepr(conftestnames=[], optnames=['verbose']) config = py.test.config._reparse([tmp.dirpath()]) py.test.raises(KeyError, "config.getvalue('x')") - config.merge_repr(repr) + config._mergerepr(repr) assert config.getvalue('x') == 1 - config.merge_repr(repr2) + config._mergerepr(repr2) assert config.option.verbose == 42 def test_config_marshability(): @@ -168,11 +168,11 @@ tmp.ensure("__init__.py") tmp.ensure("conftest.py").write("a = object()") config = py.test.config._reparse([tmp]) - py.test.raises(ValueError, "config.make_repr(conftestnames=['a'])") + py.test.raises(ValueError, "config._makerepr(conftestnames=['a'])") config.option.hello = lambda x: None - py.test.raises(ValueError, "config.make_repr(conftestnames=[])") - config.make_repr(conftestnames=[], optnames=[]) + py.test.raises(ValueError, "config._makerepr(conftestnames=[])") + config._makerepr(conftestnames=[], optnames=[]) def test_config_rconfig(): tmp = py.test.ensuretemp("rconfigopt") @@ -186,10 +186,10 @@ """)) config = py.test.config._reparse([tmp, "-G", "11"]) assert config.option.gdest == 11 - repr = config.make_repr(conftestnames=[]) + repr = config._makerepr(conftestnames=[]) config = py.test.config._reparse([tmp.dirpath()]) py.test.raises(AttributeError, "config.option.gdest") - config.merge_repr(repr) + config._mergerepr(repr) assert config.option.gdest == 11 class TestSessionAndOptions: From hpk at codespeak.net Mon Feb 12 13:21:18 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 12 Feb 2007 13:21:18 +0100 (CET) Subject: [py-svn] r38549 - in py/trunk/py/test: . rsession terminal testing Message-ID: <20070212122118.23637100A7@code0.codespeak.net> Author: hpk Date: Mon Feb 12 13:21:16 2007 New Revision: 38549 Modified: py/trunk/py/test/config.py py/trunk/py/test/rsession/slave.py py/trunk/py/test/terminal/remote.py py/trunk/py/test/testing/test_config.py Log: also privatizing Config.initdirect (which relates to remote reprs and such) Modified: py/trunk/py/test/config.py ============================================================================== --- py/trunk/py/test/config.py (original) +++ py/trunk/py/test/config.py Mon Feb 12 13:21:16 2007 @@ -52,7 +52,7 @@ self.topdir = gettopdir(args) self.args = args - def initdirect(self, topdir, repr, coltrails=None): + def _initdirect(self, topdir, repr, coltrails=None): assert not self._initialized self._initialized = True self.topdir = py.path.local(topdir) Modified: py/trunk/py/test/rsession/slave.py ============================================================================== --- py/trunk/py/test/rsession/slave.py (original) +++ py/trunk/py/test/rsession/slave.py Mon Feb 12 13:21:16 2007 @@ -90,7 +90,7 @@ import py config = py.test.config assert not config._initialized - config.initdirect(basedir, config_repr) + config._initdirect(basedir, config_repr) if hasattr(os, 'nice'): nice_level = config.getvalue('dist_nicelevel') os.nice(nice_level) Modified: py/trunk/py/test/terminal/remote.py ============================================================================== --- py/trunk/py/test/terminal/remote.py (original) +++ py/trunk/py/test/terminal/remote.py Mon Feb 12 13:21:16 2007 @@ -124,7 +124,7 @@ topdir, repr, failures = channel.receive() print "SLAVE: received configuration, using topdir:", topdir config = py.test.config - config.initdirect(topdir, repr, failures) + config._initdirect(topdir, repr, failures) config.option.session = None config.option.looponfailing = False config.option.usepdb = False Modified: py/trunk/py/test/testing/test_config.py ============================================================================== --- py/trunk/py/test/testing/test_config.py (original) +++ py/trunk/py/test/testing/test_config.py Mon Feb 12 13:21:16 2007 @@ -123,7 +123,7 @@ def test_config_init_direct(): - tmp = py.test.ensuretemp("initdirect") + tmp = py.test.ensuretemp("_initdirect") tmp.ensure("__init__.py") tmp.ensure("conftest.py").write("x=1 ; y=2") hello = tmp.ensure("test_hello.py") @@ -131,13 +131,13 @@ repr = config._makerepr(conftestnames=['x', 'y']) config2 = py.test.config._reparse([tmp.dirpath()]) config2._initialized = False # we have to do that from tests - config2.initdirect(topdir=tmp.dirpath(), repr=repr) + config2._initdirect(topdir=tmp.dirpath(), repr=repr) for col1, col2 in zip(config.getcolitems(), config2.getcolitems()): assert col1.fspath == col2.fspath - py.test.raises(AssertionError, "config2.initdirect(None, None)") + py.test.raises(AssertionError, "config2._initdirect(None, None)") from py.__.test.config import Config config3 = Config() - config3.initdirect(topdir=tmp.dirpath(), repr=repr, + config3._initdirect(topdir=tmp.dirpath(), repr=repr, coltrails=[(tmp.basename, (hello.basename,))]) assert config3.getvalue('x') == 1 assert config3.getvalue('y') == 2 From fijal at codespeak.net Mon Feb 12 14:14:37 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Mon, 12 Feb 2007 14:14:37 +0100 (CET) Subject: [py-svn] r38559 - py/trunk/py/test/rsession Message-ID: <20070212131437.6D9C4100B5@code0.codespeak.net> Author: fijal Date: Mon Feb 12 14:14:36 2007 New Revision: 38559 Modified: py/trunk/py/test/rsession/repevent.py py/trunk/py/test/rsession/reporter.py Log: Give info about tests in case of failure/interruption Modified: py/trunk/py/test/rsession/repevent.py ============================================================================== --- py/trunk/py/test/rsession/repevent.py (original) +++ py/trunk/py/test/rsession/repevent.py Mon Feb 12 14:14:36 2007 @@ -137,7 +137,9 @@ self.result = result class InterruptedExecution(ReportEvent): - pass + def __init__(self): + self.timeend = time.time() class CrashedExecution(ReportEvent): - pass + def __init__(self): + self.timeend = time.time() Modified: py/trunk/py/test/rsession/reporter.py ============================================================================== --- py/trunk/py/test/rsession/reporter.py (original) +++ py/trunk/py/test/rsession/reporter.py Mon Feb 12 14:14:36 2007 @@ -119,7 +119,10 @@ self.hangs() self.summary() return len(self.failed_tests_outcome) > 0 - + + report_InterruptedExecution = report_TestFinished + report_CrashedExecution = report_TestFinished + def hangs(self): h = [] if self.config.option.exitfirst: From fijal at codespeak.net Mon Feb 12 14:20:50 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Mon, 12 Feb 2007 14:20:50 +0100 (CET) Subject: [py-svn] r38560 - in py/trunk/py/test: . testing Message-ID: <20070212132050.EC375100AC@code0.codespeak.net> Author: fijal Date: Mon Feb 12 14:20:49 2007 New Revision: 38560 Modified: py/trunk/py/test/collect.py py/trunk/py/test/testing/test_config.py Log: Compare collectors by id rather than by name. It needs to be rethought a bit. Modified: py/trunk/py/test/collect.py ============================================================================== --- py/trunk/py/test/collect.py (original) +++ py/trunk/py/test/collect.py Mon Feb 12 14:20:49 2007 @@ -65,10 +65,11 @@ return "<%s %r>" %(self.__class__.__name__, self.name) def __eq__(self, other): - try: - return self.name == other.name and self.parent == other.parent - except AttributeError: - return False + #try: + # return self.name == other.name and self.parent == other.parent + #except AttributeError: + # return False + return self is other def __hash__(self): return hash((self.name, self.parent)) @@ -347,7 +348,7 @@ return self._name2items.get(name, None) -class Module(FSCollector, PyCollectorMixin): +class Module(FSCollector, PyCollectorMixin): def run(self): if getattr(self.obj, 'disabled', 0): return [] @@ -358,7 +359,7 @@ if res is None: attr = getattr(self.obj, name, None) if attr is not None: - res = self.makeitem(name, attr, usefilters=False) + return self.makeitem(name, attr, usefilters=False) return res def startcapture(self): @@ -403,9 +404,9 @@ return [] return ["()"] - def join(self, name): - assert name == '()' - return self.Instance(name, self) + def join(self, name): + assert name == '()' + return self.Instance(name, self) def setup(self): setup_class = getattr(self.obj, 'setup_class', None) Modified: py/trunk/py/test/testing/test_config.py ============================================================================== --- py/trunk/py/test/testing/test_config.py (original) +++ py/trunk/py/test/testing/test_config.py Mon Feb 12 14:20:49 2007 @@ -397,7 +397,8 @@ assert trail[0] == a.relto(config.topdir) assert trail[1] == ('trail.py',) col2 = config._getcollector(trail) - assert col2.listchain() == col.listchain() + assert [i.name for i in col2.listchain()] == \ + [i.name for i in col.listchain()] def test_get_collector_trail_topdir_and_beyond(self): config = py.test.config._reparse([self.tmpdir]) From hpk at codespeak.net Mon Feb 12 15:58:15 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 12 Feb 2007 15:58:15 +0100 (CET) Subject: [py-svn] r38568 - in py/trunk/py/test: . testing Message-ID: <20070212145815.7EB88100AB@code0.codespeak.net> Author: hpk Date: Mon Feb 12 15:58:14 2007 New Revision: 38568 Modified: py/trunk/py/test/collect.py py/trunk/py/test/testing/test_config.py Log: improving r38560 a bit Modified: py/trunk/py/test/collect.py ============================================================================== --- py/trunk/py/test/collect.py (original) +++ py/trunk/py/test/collect.py Mon Feb 12 15:58:14 2007 @@ -65,10 +65,8 @@ return "<%s %r>" %(self.__class__.__name__, self.name) def __eq__(self, other): - #try: - # return self.name == other.name and self.parent == other.parent - #except AttributeError: - # return False + # XXX a rather strict check for now to not confuse + # the SetupState.prepare() logic return self is other def __hash__(self): @@ -359,7 +357,7 @@ if res is None: attr = getattr(self.obj, name, None) if attr is not None: - return self.makeitem(name, attr, usefilters=False) + res = self.makeitem(name, attr, usefilters=False) return res def startcapture(self): Modified: py/trunk/py/test/testing/test_config.py ============================================================================== --- py/trunk/py/test/testing/test_config.py (original) +++ py/trunk/py/test/testing/test_config.py Mon Feb 12 15:58:14 2007 @@ -397,8 +397,7 @@ assert trail[0] == a.relto(config.topdir) assert trail[1] == ('trail.py',) col2 = config._getcollector(trail) - assert [i.name for i in col2.listchain()] == \ - [i.name for i in col.listchain()] + assert col2.listnames() == col.listnames() def test_get_collector_trail_topdir_and_beyond(self): config = py.test.config._reparse([self.tmpdir]) From hpk at codespeak.net Mon Feb 12 16:00:27 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 12 Feb 2007 16:00:27 +0100 (CET) Subject: [py-svn] r38569 - py/dist/py Message-ID: <20070212150027.250D5100AC@code0.codespeak.net> Author: hpk Date: Mon Feb 12 16:00:26 2007 New Revision: 38569 Removed: py/dist/py/ Log: remove for merge-from-trunk From hpk at codespeak.net Mon Feb 12 16:00:28 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 12 Feb 2007 16:00:28 +0100 (CET) Subject: [py-svn] r38570 - py/dist/py Message-ID: <20070212150028.B934B100B4@code0.codespeak.net> Author: hpk Date: Mon Feb 12 16:00:27 2007 New Revision: 38570 Added: py/dist/py/ - copied from r38569, py/trunk/py/ Log: use new py-trunk From guido at codespeak.net Mon Feb 12 16:21:37 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Mon, 12 Feb 2007 16:21:37 +0100 (CET) Subject: [py-svn] r38571 - in py/trunk/py/apigen: . testing Message-ID: <20070212152137.6CAB310094@code0.codespeak.net> Author: guido Date: Mon Feb 12 16:21:35 2007 New Revision: 38571 Modified: py/trunk/py/apigen/htmlgen.py py/trunk/py/apigen/testing/test_apigen_example.py py/trunk/py/apigen/todo-apigen.txt Log: Using wcpath.info().created_rev (last changed revision) rather than wcpath.status().rev (current wc rev) as revision, added revision for source and namespace pages. Modified: py/trunk/py/apigen/htmlgen.py ============================================================================== --- py/trunk/py/apigen/htmlgen.py (original) +++ py/trunk/py/apigen/htmlgen.py Mon Feb 12 16:21:35 2007 @@ -311,9 +311,25 @@ else: tag, nav = self.build_nonpython_page(fspath) title = 'sources for %s' % (fspath.basename,) + rev = self.get_revision(fspath) + if rev: + title += ' [rev. %s]' % (rev,) reltargetpath = outputpath.relto(self.base).replace(os.path.sep, '/') self.write_page(title, reltargetpath, tag, nav) + + _revcache = {} + def get_revision(self, path): + strpath = path.strpath + if strpath in self._revcache: + return self._revcache[strpath] + wc = py.path.svnwc(path) + if wc.check(versioned=True): + rev = wc.info().created_rev + else: + rev = None + self._revcache[strpath] = rev + return rev class ApiPageBuilder(AbstractPageBuilder): """ builds the html for an api docs page """ @@ -528,9 +544,13 @@ else: reltargetpath = 'api/%s.html' % (dotted_name,) self.linker.set_link(dotted_name, reltargetpath) + title_name = dotted_name if dotted_name == '': - dotted_name = self.dsa.get_module_name().split('/')[-1] - title = 'index of %s namespace' % (dotted_name,) + title_name = self.dsa.get_module_name() + title = 'index of %s' % (title_name,) + rev = self.get_revision(dotted_name) + if rev: + title += ' [rev. %s]' % (rev,) self.write_page(title, reltargetpath, tag, nav) return passed @@ -697,18 +717,35 @@ obj = get_obj(self.dsa, self.pkg, dotted_name) return getattr(obj, '__apigen_hide_from_nav__', False) + _revcache = {} + def get_proj_revision(self): + if '' in self._revcache: + return self._revcache[''] + wc = py.path.svnwc(self.projpath) + if wc.check(versioned=True): + rev = wc.info().created_rev + else: + rev = None + self._revcache[''] = rev + return rev + def get_revision(self, dotted_name): + if dotted_name in self._revcache: + return self._revcache[dotted_name] obj = get_obj(self.dsa, self.pkg, dotted_name) + rev = None try: sourcefile = inspect.getsourcefile(obj) except TypeError: - return None - if sourcefile is None: - return None - if sourcefile[-1] in ['o', 'c']: - sourcefile = sourcefile[:-1] - wc = py.path.svnwc(sourcefile) - if wc.check(versioned=True): - return wc.status().rev - return None + pass + else: + if sourcefile is not None: + if sourcefile[-1] in ['o', 'c']: + sourcefile = sourcefile[:-1] + wc = py.path.svnwc(sourcefile) + if wc.check(versioned=True): + rev = wc.info().created_rev + rev = rev or self.get_proj_revision() + self._revcache[dotted_name] = rev + return rev Modified: py/trunk/py/apigen/testing/test_apigen_example.py ============================================================================== --- py/trunk/py/apigen/testing/test_apigen_example.py (original) +++ py/trunk/py/apigen/testing/test_apigen_example.py Mon Feb 12 16:21:35 2007 @@ -8,6 +8,7 @@ from py.__.apigen.project import Project from py.__.test.web import webcheck from py.__.apigen.conftest import option +from py.__.path.svn.testing.svntestbase import make_test_repo def run_string_sequence_test(data, seq): currpos = -1 @@ -21,8 +22,7 @@ py.test.fail('string %r: %s' % (s, message)) currpos = newpos -def setup_fs_project(): - temp = py.test.ensuretemp('apigen_example') +def setup_fs_project(temp): temp.ensure("pkg/func.py").write(py.code.Source("""\ def func(arg1): "docstring" @@ -96,7 +96,8 @@ class AbstractBuilderTest(object): def setup_class(cls): - cls.fs_root, cls.pkg_name = setup_fs_project() + temp = py.test.ensuretemp('apigen_example') + cls.fs_root, cls.pkg_name = setup_fs_project(temp) cls.ds, cls.dsa = get_dsa(cls.fs_root, cls.pkg_name) cls.project = Project() @@ -240,14 +241,14 @@ html = mainfile.read() print html run_string_sequence_test(html, [ - 'index of main namespace', + 'index of main', ]) otherfile = self.base.join('api/other.html') assert otherfile.check() otherhtml = otherfile.read() print otherhtml run_string_sequence_test(otherhtml, [ - 'index of other namespace', + 'index of other', ]) _checkhtml(html) _checkhtml(otherhtml) @@ -257,7 +258,7 @@ pkgfile = self.base.join('api/index.html') assert pkgfile.check() html = pkgfile.read() - assert 'index of project pkg namespace' + assert 'index of pkg' in html _checkhtml(html) def test_build_namespace_pages_subnamespace(self): @@ -318,6 +319,66 @@ assert '' in html _checkhtml(html) + def test_get_revision(self): + # XXX a lot of setup required for this one... more like a functional + # test I fear + + # create test repo and checkout + repo = make_test_repo('test_get_revision_api_repo') + wc = py.path.svnwc(py.test.ensuretemp('test_get_revision_api_wc')) + wc.checkout(repo.url) + assert wc.status().rev == '0' + + # create a temp package inside the working copy + fs_root, pkg_name = setup_fs_project(wc) + ds, dsa = get_dsa(self.fs_root, self.pkg_name) + wc.commit('test get revision commit') + wc.update() + + # clear cache + py.__.apigen.htmlgen._get_obj_cache = {} + + # fiddle about a bit with paths so that our package is picked up :| + old_path = py.std.sys.path + try: + py.std.sys.path.insert(0, fs_root.strpath) + pkgkeys = [k for k in py.std.sys.modules.keys() if + k == 'pkg' or k.startswith('pkg.')] + # remove modules from sys.modules + for key in pkgkeys: + del py.std.sys.modules[key] + + # now create a new apb that uses the wc pkg + apb = ApiPageBuilder(self.base, self.linker, dsa, + fs_root.join(pkg_name), + self.namespace_tree, self.project) + apb._revcache = {} # clear cache, this is on class level!! + + pkg = wc.join('pkg') + assert pkg.check(versioned=True) + assert pkg.info().created_rev == 1 + + funcpath = pkg.join('func.py') + classpath = pkg.join('someclass.py') + assert funcpath.check(versioned=True) + assert classpath.check(versioned=True) + assert apb.get_revision('main.sub.func') == 1 + assert apb.get_revision('main.SomeClass') == 1 + assert apb.get_revision('') == 1 + assert apb.get_revision('main.sub') == 1 + funcpath.write(funcpath.read() + '\n') + funcpath.commit('updated func') + wc.update() + apb._revcache = {} # clear cache + assert apb.get_revision('main.sub.func') == 2 + assert apb.get_revision('') == 1 + assert apb.get_revision('main.SomeClass') == 1 + finally: + py.std.sys.path = old_path + # clear caches again + py.__.apigen.htmlgen._get_obj_cache = {} + apb._revcache = {} + class TestSourcePageBuilder(AbstractBuilderTest): def test_build_pages(self): self.spb.build_pages(self.fs_root) @@ -376,3 +437,25 @@ 'href="somesubclass.py.html">somesubclass.py', ]) + def test_get_revision(self): + repo = make_test_repo('test_get_revision_source_repo') + wc = py.path.svnwc(py.test.ensuretemp('test_get_revision_source_wc')) + wc.checkout(repo.url) + + dir = wc.ensure('dir', dir=True) + file = dir.ensure('file.py', file=True) + wc.commit('added dir and file') + wc.update() + assert file.check(versioned=True) + assert wc.status().rev == '1' + + assert self.spb.get_revision(dir) == 1 + assert self.spb.get_revision(file) == 1 + + file.write('while 1:\n print "py lib is cool\n"') + file.commit('added some code') + assert file.status().rev == '2' + self.spb._revcache = {} + assert self.spb.get_revision(file) == 2 + assert self.spb.get_revision(dir) == 1 + Modified: py/trunk/py/apigen/todo-apigen.txt ============================================================================== --- py/trunk/py/apigen/todo-apigen.txt (original) +++ py/trunk/py/apigen/todo-apigen.txt Mon Feb 12 16:21:35 2007 @@ -12,7 +12,5 @@ NOT YET DONE (later?) -* add SVN info to the (source only?) pages - * add warning about py.test possibly not covering the whole API From fijal at codespeak.net Mon Feb 12 16:27:48 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Mon, 12 Feb 2007 16:27:48 +0100 (CET) Subject: [py-svn] r38572 - py/trunk/py/test/rsession Message-ID: <20070212152748.1296B10094@code0.codespeak.net> Author: fijal Date: Mon Feb 12 16:27:46 2007 New Revision: 38572 Modified: py/trunk/py/test/rsession/outcome.py Log: Move a bit into using high-level traceback interface instead of low-level one Modified: py/trunk/py/test/rsession/outcome.py ============================================================================== --- py/trunk/py/test/rsession/outcome.py (original) +++ py/trunk/py/test/rsession/outcome.py Mon Feb 12 16:27:46 2007 @@ -29,10 +29,17 @@ tb_info = [self.traceback_entry_repr(x, tbstyle) for x in excinfo.traceback] rec_index = excinfo.traceback.recursionindex() - etype = excinfo.type - if hasattr(etype, '__name__'): - etype = etype.__name__ - return (etype, str(excinfo.value), (tb_info, rec_index)) + if hasattr(excinfo, 'type'): + etype = excinfo.type + if hasattr(etype, '__name__'): + etype = etype.__name__ + else: + etype = excinfo.typename + val = getattr(excinfo, 'value', None) + if not val: + val = exinfo.exconly() + val = str(val) + return (etype, val, (tb_info, rec_index)) def traceback_entry_repr(self, tb_entry, tb_style): lineno = tb_entry.lineno @@ -46,7 +53,7 @@ source = str(tb_entry.getsource()).split("\n")[relline] except py.error.ENOENT: source = "[cannot get source]" - name = tb_entry.frame.code.raw.co_name + name = str(tb_entry.frame.code.name) # XXX: Bare except. What can getsource() raise anyway? # SyntaxError, AttributeError, IndentationError for sure, check it #except: From fijal at codespeak.net Mon Feb 12 16:28:10 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Mon, 12 Feb 2007 16:28:10 +0100 (CET) Subject: [py-svn] r38573 - py/trunk/py/test/rsession/testing Message-ID: <20070212152810.3724710093@code0.codespeak.net> Author: fijal Date: Mon Feb 12 16:28:09 2007 New Revision: 38573 Modified: py/trunk/py/test/rsession/testing/test_web.py Log: This tests was completely broken when pypy is there Modified: py/trunk/py/test/rsession/testing/test_web.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_web.py (original) +++ py/trunk/py/test/rsession/testing/test_web.py Mon Feb 12 16:28:09 2007 @@ -8,6 +8,8 @@ try: from pypy.translator.js.main import rpython2javascript from pypy.translator.js import commproxy + mod.commproxy = commproxy + mod.rpython2javascript = rpython2javascript except ImportError: py.test.skip("PyPy not found") mod.commproxy.USE_MOCHIKIT = False From fijal at codespeak.net Mon Feb 12 16:28:37 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Mon, 12 Feb 2007 16:28:37 +0100 (CET) Subject: [py-svn] r38574 - py/trunk/py/test/rsession Message-ID: <20070212152837.E7F2C10071@code0.codespeak.net> Author: fijal Date: Mon Feb 12 16:28:28 2007 New Revision: 38574 Modified: py/trunk/py/test/rsession/executor.py Log: Special case outcome.Failed - reuse that excinfo Modified: py/trunk/py/test/rsession/executor.py ============================================================================== --- py/trunk/py/test/rsession/executor.py (original) +++ py/trunk/py/test/rsession/executor.py Mon Feb 12 16:28:28 2007 @@ -1,7 +1,7 @@ """ Remote executor """ -import py, os +import py, os, sys from py.__.test.rsession.outcome import Outcome, ReprOutcome from py.__.test.rsession.box import Box @@ -39,11 +39,15 @@ except (SystemExit, KeyboardInterrupt): raise except: - excinfo = py.code.ExceptionInfo() - if isinstance(self.item, py.test.Function): - fun = self.item.obj # hope this is stable - code = py.code.Code(fun) - excinfo.traceback = excinfo.traceback.cut( + e = sys.exc_info()[1] + if isinstance(e, Failed): + excinfo = e.excinfo + else: + excinfo = py.code.ExceptionInfo() + if isinstance(self.item, py.test.Function): + fun = self.item.obj # hope this is stable + code = py.code.Code(fun) + excinfo.traceback = excinfo.traceback.cut( path=code.path, firstlineno=code.firstlineno) outcome = Outcome(excinfo=excinfo, setupfailure=False) if self.usepdb: From fijal at codespeak.net Mon Feb 12 16:30:32 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Mon, 12 Feb 2007 16:30:32 +0100 (CET) Subject: [py-svn] r38576 - py/trunk/py/test/rsession Message-ID: <20070212153032.0363610094@code0.codespeak.net> Author: fijal Date: Mon Feb 12 16:30:32 2007 New Revision: 38576 Modified: py/trunk/py/test/rsession/outcome.py Log: typo! Modified: py/trunk/py/test/rsession/outcome.py ============================================================================== --- py/trunk/py/test/rsession/outcome.py (original) +++ py/trunk/py/test/rsession/outcome.py Mon Feb 12 16:30:32 2007 @@ -37,7 +37,7 @@ etype = excinfo.typename val = getattr(excinfo, 'value', None) if not val: - val = exinfo.exconly() + val = excinfo.exconly() val = str(val) return (etype, val, (tb_info, rec_index)) From fijal at codespeak.net Mon Feb 12 16:40:05 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Mon, 12 Feb 2007 16:40:05 +0100 (CET) Subject: [py-svn] r38577 - py/trunk/py/test/rsession/testing Message-ID: <20070212154005.E2FCF1009A@code0.codespeak.net> Author: fijal Date: Mon Feb 12 16:40:04 2007 New Revision: 38577 Modified: py/trunk/py/test/rsession/testing/test_executor.py Log: Test for explicit Failed Modified: py/trunk/py/test/rsession/testing/test_executor.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_executor.py (original) +++ py/trunk/py/test/rsession/testing/test_executor.py Mon Feb 12 16:40:04 2007 @@ -6,6 +6,7 @@ AsyncExecutor, ApigenExecutor from py.__.test.rsession.outcome import ReprOutcome from py.__.test.rsession.testing.basetest import BasicRsessionTest +from py.__.test.outcome import Failed def setup_module(mod): if py.std.sys.platform == "win32": @@ -32,6 +33,10 @@ def run(self): print "hello" +class ItemTestFailingExplicit(Item): + def run(self): + raise Failed(excinfo="3") + class TestExecutor(BasicRsessionTest): def test_run_executor(self): ex = RunExecutor(ItemTestPassing("pass", self.config), config=self.config) @@ -145,3 +150,11 @@ assert out1.passed assert out2.passed assert not out3.passed + + def test_executor_explicit_Failed(self): + ex = RunExecutor(ItemTestFailingExplicit("failex", self.config), + config=self.config) + + outcome = ex.execute() + assert not outcome.passed + assert outcome.excinfo == "3" From guido at codespeak.net Mon Feb 12 16:45:57 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Mon, 12 Feb 2007 16:45:57 +0100 (CET) Subject: [py-svn] r38578 - py/trunk/py/apigen Message-ID: <20070212154557.AF6CA1009A@code0.codespeak.net> Author: guido Date: Mon Feb 12 16:45:56 2007 New Revision: 38578 Modified: py/trunk/py/apigen/html.py py/trunk/py/apigen/style.css py/trunk/py/apigen/todo.txt Log: Made that the docstring isn't replaced by the function info anymore, but the func info is appended, some small style issues. Modified: py/trunk/py/apigen/html.py ============================================================================== --- py/trunk/py/apigen/html.py (original) +++ py/trunk/py/apigen/html.py Mon Feb 12 16:45:56 2007 @@ -63,15 +63,14 @@ fd = H.FunctionDef(localname, argdesc, onclick=('showhideel(' 'document.getElementById("%s")); ' - 'showhideel(' - 'document.getElementById("%s")); ' - 'this.scrollIntoView()' % ( - infoid, docstringid))) - ds = H.Docstring(docstring or '*no docstring available*', - id=docstringid) - fi = H.FunctionInfo(valuedesc, csource, callstack, - id=infoid, style="display: none") - super(H.FunctionDescription, self).__init__(fd, ds, fi) + % (infoid,))) + infodiv = H.div( + H.Docstring(docstring or '*no docstring available*', + id=docstringid), + H.FunctionInfo(valuedesc, csource, callstack, + id=infoid, style="display: none"), + class_='funcdocinfo') + super(H.FunctionDescription, self).__init__(fd, infodiv) class FunctionDef(html.h2): style = html.Style(cursor='pointer') Modified: py/trunk/py/apigen/style.css ============================================================================== --- py/trunk/py/apigen/style.css (original) +++ py/trunk/py/apigen/style.css Mon Feb 12 16:45:56 2007 @@ -48,6 +48,10 @@ color: blue; } +h2.funcdef:hover { + text-decoration: underline; +} + .code a { color: blue; font-weight: bold; @@ -91,7 +95,7 @@ color: green; } -.funcinfo { +.funcdocinfo { border: 1px solid black; color: black; padding: 1em; Modified: py/trunk/py/apigen/todo.txt ============================================================================== --- py/trunk/py/apigen/todo.txt (original) +++ py/trunk/py/apigen/todo.txt Mon Feb 12 16:45:56 2007 @@ -3,10 +3,14 @@ py/apigen sources [rev XXX] + DONE + * and "namespace" pages: builtin namespace index [rev XXX] + DONE, except they're now called 'index of [rev. XXX]' + * get konqueror to display indents in source code better? (currently it doesn't look like more than a single space) @@ -29,6 +33,8 @@ XXX it's nice but can you keep the docstring visible when more information is displayed/toggled? + DONE too + * allow for flexibility regarding linking from py/doc/*.txt documents to apigen with respect to where apigen/ docs are located. From hpk at codespeak.net Mon Feb 12 16:55:49 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 12 Feb 2007 16:55:49 +0100 (CET) Subject: [py-svn] r38579 - in py/trunk/py/execnet: . testing Message-ID: <20070212155549.5ACD01009C@code0.codespeak.net> Author: hpk Date: Mon Feb 12 16:55:48 2007 New Revision: 38579 Modified: py/trunk/py/execnet/gateway.py py/trunk/py/execnet/testing/test_gateway.py Log: library code should not use magic timeouts, testing code may use a global TESTTIMEOUT Modified: py/trunk/py/execnet/gateway.py ============================================================================== --- py/trunk/py/execnet/gateway.py (original) +++ py/trunk/py/execnet/gateway.py Mon Feb 12 16:55:48 2007 @@ -241,7 +241,7 @@ channel.send(outchannel) clist.append(channel) for c in clist: - c.waitclose(1.0) + c.waitclose() class Handle: def close(_): for name, out in ('stdout', stdout), ('stderr', stderr): @@ -250,7 +250,7 @@ import sys channel.gateway._ThreadOut(sys, %r).resetdefault() """ % name) - c.waitclose(1.0) + c.waitclose() return Handle() def exit(self): Modified: py/trunk/py/execnet/testing/test_gateway.py ============================================================================== --- py/trunk/py/execnet/testing/test_gateway.py (original) +++ py/trunk/py/execnet/testing/test_gateway.py Mon Feb 12 16:55:48 2007 @@ -8,6 +8,8 @@ from StringIO import StringIO from py.__.execnet.register import startup_modules, getsource +TESTTIMEOUT = 10.0 # seconds + def test_getsource_import_modules(): for dottedname in startup_modules: yield getsource, dottedname @@ -98,11 +100,11 @@ def test_remote_exec_waitclose(self): channel = self.gw.remote_exec('pass') - channel.waitclose(timeout=5.0) + channel.waitclose(TESTTIMEOUT) def test_remote_exec_waitclose_2(self): channel = self.gw.remote_exec('def gccycle(): pass') - channel.waitclose(timeout=5.0) + channel.waitclose(TESTTIMEOUT) def test_remote_exec_waitclose_noarg(self): channel = self.gw.remote_exec('pass') @@ -110,7 +112,7 @@ def test_remote_exec_error_after_close(self): channel = self.gw.remote_exec('pass') - channel.waitclose(timeout=5.0) + channel.waitclose(TESTTIMEOUT) py.test.raises(IOError, channel.send, 0) def test_remote_exec_channel_anonymous(self): @@ -190,12 +192,12 @@ assert x == 42 # check that the both sides previous channels are really gone - channel.waitclose(0.3) + channel.waitclose(TESTTIMEOUT) #assert c.id not in self.gw._channelfactory newchan = self.gw.remote_exec(''' assert %d not in channel.gateway._channelfactory._channels ''' % (channel.id)) - newchan.waitclose(0.3) + newchan.waitclose(TESTTIMEOUT) assert channel.id not in self.gw._channelfactory._channels def test_channel_receiver_callback(self): @@ -208,7 +210,7 @@ ''') channel.setcallback(callback=l.append) py.test.raises(IOError, channel.receive) - channel.waitclose(1.0) + channel.waitclose(TESTTIMEOUT) assert len(l) == 3 assert l[:2] == [42,13] assert isinstance(l[2], channel.__class__) @@ -224,7 +226,7 @@ assert x == 42 channel.setcallback(callback=l.append) py.test.raises(IOError, channel.receive) - channel.waitclose(1.0) + channel.waitclose(TESTTIMEOUT) assert len(l) == 2 assert l[0] == 13 assert isinstance(l[1], channel.__class__) @@ -238,7 +240,7 @@ channel.send(42) ''') channel.setcallback(callback) - channel.waitclose(1.0) + channel.waitclose(TESTTIMEOUT) assert l == [42] def test_channel_callback_stays_active(self, earlyfree=True): @@ -273,7 +275,7 @@ def test_channel_callback_remote_freed(self): channel = self.test_channel_callback_stays_active(False) - channel.waitclose(1.0) # freed automatically at the end of producer() + channel.waitclose(TESTTIMEOUT) # freed automatically at the end of producer() def test_channel_endmarker_callback(self): l = [] @@ -284,7 +286,7 @@ ''') channel.setcallback(l.append, 999) py.test.raises(IOError, channel.receive) - channel.waitclose(1.0) + channel.waitclose(TESTTIMEOUT) assert len(l) == 4 assert l[:2] == [42,13] assert isinstance(l[2], channel.__class__) @@ -294,7 +296,7 @@ out = py.std.StringIO.StringIO() handle = self.gw._remote_redirect(stdout=out) c = self.gw.remote_exec("print 42") - c.waitclose(1.0) + c.waitclose(TESTTIMEOUT) handle.close() s = out.getvalue() assert s.strip() == "42" @@ -306,7 +308,7 @@ stdout=l[i].append) for i in range(num)] for x in channels: - x.waitclose(5.0) + x.waitclose(TESTTIMEOUT) for i in range(num): subl = l[i] @@ -329,7 +331,7 @@ def test_channel_file_write_error(self): channel = self.gw.remote_exec("pass") f = channel.makefile() - channel.waitclose(1.0) + channel.waitclose(TESTTIMEOUT) py.test.raises(IOError, f.write, 'hello') def test_channel_file_proxyclose(self): @@ -441,9 +443,9 @@ time.sleep(0.5) os.kill(remote, signal.SIGKILL) time.sleep(1) - channel.waitclose(1.0) + channel.waitclose(TESTTIMEOUT) py.test.raises(EOFError, channel.receive) - #channel.waitclose(1.0) + #channel.waitclose(TESTTIMEOUT) #channel.send('#') From hpk at codespeak.net Mon Feb 12 17:10:35 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 12 Feb 2007 17:10:35 +0100 (CET) Subject: [py-svn] r38582 - py/trunk/py/doc Message-ID: <20070212161035.538E91009B@code0.codespeak.net> Author: hpk Date: Mon Feb 12 17:10:28 2007 New Revision: 38582 Modified: py/trunk/py/doc/release-0.9.0.txt Log: refining the release announcement/finalizing from my site fijal, cfbolz, guido_w: are you ok with it? (your name is under the release announcement) Modified: py/trunk/py/doc/release-0.9.0.txt ============================================================================== --- py/trunk/py/doc/release-0.9.0.txt (original) +++ py/trunk/py/doc/release-0.9.0.txt Mon Feb 12 17:10:28 2007 @@ -1,7 +1,9 @@ -py lib 0.9.0: py.test, distributed execution and dev support +py lib 0.9.0: py.test, distributed execution, greenlets and more ====================================================================== -Welcome to the 0.9.0 py lib (including py.test) release! +Welcome to the 0.9.0 py lib release - a library aiming to +support agile and test-driven python development on various levels. + Main API/Tool Features: * py.test: cross-project testing tool with many advanced features @@ -12,7 +14,7 @@ * tested against Win32, Linux and OSX All these features and their API have extensive documentation, -generated with "apigen", which we intend to make accessible +generated with the new "apigen", which we intend to make accessible for other python projects as well. Here is the entry point for installing the py lib: @@ -23,7 +25,10 @@ http://codespeak.net/py/XXX # figure out exact scheme -best and have fun and let us know what you think! +The work on the py lib has been partially funded by the +European Union and merlinux gmbh within the PyPy project. + +best, have fun and let us know what you think! holger krekel, Maciej Fijalkowski, Guido Wesdorp, Carl Friedrich Bolz From hpk at codespeak.net Mon Feb 12 17:13:06 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 12 Feb 2007 17:13:06 +0100 (CET) Subject: [py-svn] r38584 - py/trunk/py/doc Message-ID: <20070212161306.39CFD100A6@code0.codespeak.net> Author: hpk Date: Mon Feb 12 17:13:05 2007 New Revision: 38584 Removed: py/trunk/py/doc/home.txt Modified: py/trunk/py/doc/confrest.py py/trunk/py/doc/index.txt Log: cleaning up menu bar: "index" instead of doc, removing pseudo "home" page. Modified: py/trunk/py/doc/confrest.py ============================================================================== --- py/trunk/py/doc/confrest.py (original) +++ py/trunk/py/doc/confrest.py Mon Feb 12 17:13:05 2007 @@ -33,8 +33,7 @@ media="screen", rel="stylesheet", type="text/css")) self.menubar = html.div( - html.a("home", href="home.html", class_="menu"), " ", - html.a("doc", href="index.html", class_="menu"), " ", + html.a("index", href="index.html", class_="menu"), " ", html.a("api", href=apigen_relpath + "api/index.html", class_="menu"), " ", html.a("source", href=apigen_relpath + "source/index.html", Deleted: /py/trunk/py/doc/home.txt ============================================================================== --- /py/trunk/py/doc/home.txt Mon Feb 12 17:13:05 2007 +++ (empty file) @@ -1,6 +0,0 @@ - - The py lib aims at supporting a decent development process - addressing important deployment, versioning, testing and - documentation issues - seen primarily from the perspective - of a FOSS (Free and Open Source) developer. - Modified: py/trunk/py/doc/index.txt ============================================================================== --- py/trunk/py/doc/index.txt (original) +++ py/trunk/py/doc/index.txt Mon Feb 12 17:13:05 2007 @@ -1,6 +1,10 @@ py lib documentation ================================================= + The py lib aims at supporting a decent development process + addressing deployment, versioning, testing and documentation + perspectives. + `Download and Installation`_ Main tools and API From cfbolz at codespeak.net Mon Feb 12 17:17:37 2007 From: cfbolz at codespeak.net (cfbolz at codespeak.net) Date: Mon, 12 Feb 2007 17:17:37 +0100 (CET) Subject: [py-svn] r38585 - py/trunk/py/doc Message-ID: <20070212161737.63C86100A4@code0.codespeak.net> Author: cfbolz Date: Mon Feb 12 17:17:37 2007 New Revision: 38585 Modified: py/trunk/py/doc/release-0.9.0.txt Log: remove the gmbh Modified: py/trunk/py/doc/release-0.9.0.txt ============================================================================== --- py/trunk/py/doc/release-0.9.0.txt (original) +++ py/trunk/py/doc/release-0.9.0.txt Mon Feb 12 17:17:37 2007 @@ -26,7 +26,7 @@ http://codespeak.net/py/XXX # figure out exact scheme The work on the py lib has been partially funded by the -European Union and merlinux gmbh within the PyPy project. +European Union and merlinux within the PyPy project. best, have fun and let us know what you think! From hpk at codespeak.net Mon Feb 12 17:27:09 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 12 Feb 2007 17:27:09 +0100 (CET) Subject: [py-svn] r38586 - py/trunk/py/doc Message-ID: <20070212162709.56D161009A@code0.codespeak.net> Author: hpk Date: Mon Feb 12 17:27:07 2007 New Revision: 38586 Modified: py/trunk/py/doc/release-0.9.0.txt Log: use merlinux.eu (did you know it exists? :) Modified: py/trunk/py/doc/release-0.9.0.txt ============================================================================== --- py/trunk/py/doc/release-0.9.0.txt (original) +++ py/trunk/py/doc/release-0.9.0.txt Mon Feb 12 17:27:07 2007 @@ -25,8 +25,8 @@ http://codespeak.net/py/XXX # figure out exact scheme -The work on the py lib has been partially funded by the -European Union and merlinux within the PyPy project. +Work on the py lib has been partially funded by the +European Union and merlinux.eu within the PyPy project. best, have fun and let us know what you think! From hpk at codespeak.net Mon Feb 12 17:29:16 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 12 Feb 2007 17:29:16 +0100 (CET) Subject: [py-svn] r38588 - py/trunk/py/doc Message-ID: <20070212162916.3888D100A6@code0.codespeak.net> Author: hpk Date: Mon Feb 12 17:29:15 2007 New Revision: 38588 Modified: py/trunk/py/doc/release-0.9.0.txt Log: or maybe this version for google? :) Modified: py/trunk/py/doc/release-0.9.0.txt ============================================================================== --- py/trunk/py/doc/release-0.9.0.txt (original) +++ py/trunk/py/doc/release-0.9.0.txt Mon Feb 12 17:29:15 2007 @@ -26,7 +26,7 @@ http://codespeak.net/py/XXX # figure out exact scheme Work on the py lib has been partially funded by the -European Union and merlinux.eu within the PyPy project. +European Union and http://merlinux.de within the PyPy project. best, have fun and let us know what you think! From hpk at codespeak.net Mon Feb 12 17:35:14 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 12 Feb 2007 17:35:14 +0100 (CET) Subject: [py-svn] r38591 - py/dist/py/doc Message-ID: <20070212163514.2962610071@code0.codespeak.net> Author: hpk Date: Mon Feb 12 17:35:13 2007 New Revision: 38591 Modified: py/dist/py/doc/download.txt Log: note that there are problems for configurations where we can't drive dynamic c-compilation Modified: py/dist/py/doc/download.txt ============================================================================== --- py/dist/py/doc/download.txt (original) +++ py/dist/py/doc/download.txt Mon Feb 12 17:35:13 2007 @@ -97,6 +97,15 @@ http://codespeak.net/svn/py/release/py-0.9.x +INSTALLATION LIMITATIONS +================================= + +The py lib uses a mechanism to dynamically compile C-source +code, particularly for greenlets. If you do not have a +C-compiler that can work on the fly, then using such +modules will fail at runtime. This is often the +case for windows systems and we hope to fix/deal better +with this issue in the future. py subversion directory structure ================================= From hpk at codespeak.net Mon Feb 12 17:42:54 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 12 Feb 2007 17:42:54 +0100 (CET) Subject: [py-svn] r38595 - py/trunk/py Message-ID: <20070212164254.388E410077@code0.codespeak.net> Author: hpk Date: Mon Feb 12 17:42:53 2007 New Revision: 38595 Modified: py/trunk/py/__init__.py Log: streamlining info a bit Modified: py/trunk/py/__init__.py ============================================================================== --- py/trunk/py/__init__.py (original) +++ py/trunk/py/__init__.py Mon Feb 12 17:42:53 2007 @@ -1,4 +1,4 @@ -"""\ +""" the py lib is a development support library featuring py.test, an interactive testing tool which supports unit-testing with practically no boilerplate. @@ -8,7 +8,7 @@ version = "0.9.0-beta" initpkg(__name__, - description = "py.test and the py lib", + description = "py lib: agile development and test support library", revision = int('$LastChangedRevision$'.split(':')[1][:-1]), lastchangedate = '$LastChangedDate$', version = version, From arigo at codespeak.net Mon Feb 12 17:49:32 2007 From: arigo at codespeak.net (arigo at codespeak.net) Date: Mon, 12 Feb 2007 17:49:32 +0100 (CET) Subject: [py-svn] r38597 - py/trunk/py/misc Message-ID: <20070212164932.F025C100B3@code0.codespeak.net> Author: arigo Date: Mon Feb 12 17:49:32 2007 New Revision: 38597 Modified: py/trunk/py/misc/buildcmodule.py Log: Raise an ImportError if there is no way to build a C extension module, e.g. on top of pypy-c. Importing e.g. py.magic.greenlet then gives an exception that is saner to catch than ValueError. Modified: py/trunk/py/misc/buildcmodule.py ============================================================================== --- py/trunk/py/misc/buildcmodule.py (original) +++ py/trunk/py/misc/buildcmodule.py Mon Feb 12 17:49:32 2007 @@ -28,7 +28,7 @@ if filetype == imp.C_EXTENSION: break else: - raise ValueError, "cannot find the file name suffix of C ext modules" + raise ImportError, "cannot find the file name suffix of C ext modules" lib = dirpath.join(modname+ext) # argl! we need better "build"-locations alltogether! From hpk at codespeak.net Mon Feb 12 17:57:46 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 12 Feb 2007 17:57:46 +0100 (CET) Subject: [py-svn] r38598 - in py/trunk/py: . bin Message-ID: <20070212165746.6563B1009A@code0.codespeak.net> Author: hpk Date: Mon Feb 12 17:57:45 2007 New Revision: 38598 Modified: py/trunk/py/__init__.py py/trunk/py/bin/_makepyrelease.py Log: fixing download url and fix makepyrelease script Modified: py/trunk/py/__init__.py ============================================================================== --- py/trunk/py/__init__.py (original) +++ py/trunk/py/__init__.py Mon Feb 12 17:57:45 2007 @@ -13,7 +13,7 @@ lastchangedate = '$LastChangedDate$', version = version, url = "http://codespeak.net/py", - download_url = "http://codespeak.net/download/py/%s.tar.gz" %(version,), + download_url = "http://codespeak.net/download/py/py-%s.tar.gz" %(version,), license = "MIT license", platforms = ['unix', 'linux', 'cygwin'], author = "holger krekel, Carl Friedrich Bolz, Guido Wesdorp, Maciej Fijalkowski, Armin Rigo & others", Modified: py/trunk/py/bin/_makepyrelease.py ============================================================================== --- py/trunk/py/bin/_makepyrelease.py (original) +++ py/trunk/py/bin/_makepyrelease.py Mon Feb 12 17:57:45 2007 @@ -51,7 +51,7 @@ distdir = tmpdir.ensure('dist', dir=1) oldir = rootdir.chdir() try: - from py.__.misc.dist import setup + from py.__.misc._dist import setup trace("invoking sdist, generating into %s" % (distdir,)) setup(py, script_name="setup.py", script_args=('-q', 'sdist', '--no-prune', @@ -81,7 +81,7 @@ f = urllib.urlopen(strurl) basename = strurl.split('/')[-1] target = tmpdir.join(basename) - trace("downloading to %s" %(target,)) + trace("downloading %r to %s" %(strurl, target,)) target.write(f.read()) trace("extracting to %s" %(target,)) From hpk at codespeak.net Mon Feb 12 18:04:16 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 12 Feb 2007 18:04:16 +0100 (CET) Subject: [py-svn] r38601 - py/trunk/py/doc Message-ID: <20070212170416.BA913100AB@code0.codespeak.net> Author: hpk Date: Mon Feb 12 18:04:15 2007 New Revision: 38601 Modified: py/trunk/py/doc/download.txt Log: pointing to the just uploaded py-0.9.0-beta packages Modified: py/trunk/py/doc/download.txt ============================================================================== --- py/trunk/py/doc/download.txt (original) +++ py/trunk/py/doc/download.txt Mon Feb 12 18:04:15 2007 @@ -7,13 +7,13 @@ Downloading a tar/zip file and installing it =================================================== -The latest stable public release: +The latest public release: - `download py-0.9.0.tgz`_ - `download py-0.9.0.zip`_ + `download py-0.9.0-beta.tar.gz`_ + `download py-0.9.0-beta.zip`_ -.. _`download py-0.9.0.tgz`: http://codespeak.net/download/py/py-0.9.0.tgz -.. _`download py-0.9.0.zip`: http://codespeak.net/download/py/py-0.9.0.zip +.. _`download py-0.9.0-beta.tar.gz`: http://codespeak.net/download/py/py-0.9.0-beta.tar.gz +.. _`download py-0.9.0-beta.zip`: http://codespeak.net/download/py/py-0.9.0-beta.zip The py lib can be `globally installed via setup.py`_ or `used locally`_. From hpk at codespeak.net Mon Feb 12 18:04:34 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 12 Feb 2007 18:04:34 +0100 (CET) Subject: [py-svn] r38602 - py/dist/py Message-ID: <20070212170434.42204100B3@code0.codespeak.net> Author: hpk Date: Mon Feb 12 18:04:33 2007 New Revision: 38602 Removed: py/dist/py/ Log: remove for merge-from-trunk From hpk at codespeak.net Mon Feb 12 18:04:37 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 12 Feb 2007 18:04:37 +0100 (CET) Subject: [py-svn] r38603 - py/dist/py Message-ID: <20070212170437.77FDF100B6@code0.codespeak.net> Author: hpk Date: Mon Feb 12 18:04:35 2007 New Revision: 38603 Added: py/dist/py/ - copied from r38602, py/trunk/py/ Log: use new py-trunk From hpk at codespeak.net Mon Feb 12 18:58:39 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 12 Feb 2007 18:58:39 +0100 (CET) Subject: [py-svn] r38621 - in py/trunk/py/doc: . future Message-ID: <20070212175839.E988310098@code0.codespeak.net> Author: hpk Date: Mon Feb 12 18:58:38 2007 New Revision: 38621 Added: py/trunk/py/doc/future.txt - copied, changed from r38620, py/trunk/py/doc/future/future.txt Removed: py/trunk/py/doc/future/future.txt Modified: py/trunk/py/doc/coding-style.txt py/trunk/py/doc/conftest.py py/trunk/py/doc/contact.txt py/trunk/py/doc/index.txt py/trunk/py/doc/misc.txt py/trunk/py/doc/why_py.txt Log: * fix for linking problems: move future.txt one level up * be slightly more defensive about apigen_relpath Modified: py/trunk/py/doc/coding-style.txt ============================================================================== --- py/trunk/py/doc/coding-style.txt (original) +++ py/trunk/py/doc/coding-style.txt Mon Feb 12 18:58:38 2007 @@ -70,4 +70,4 @@ .. _`PEP 8 Style Guide for Python Code`: http://www.python.org/peps/pep-0008.html .. _`py-dev mailing list`: http://codespeak.net/mailman/listinfo/py-dev -.. _`future`: future/future.html +.. _`future`: future.html Modified: py/trunk/py/doc/conftest.py ============================================================================== --- py/trunk/py/doc/conftest.py (original) +++ py/trunk/py/doc/conftest.py Mon Feb 12 18:58:38 2007 @@ -21,7 +21,7 @@ ) def get_apigen_relpath(): - return py.test.config.option.apigen_relpath + "/" + return py.test.config.option.apigen_relpath.rstrip('\/') + "/" def deindent(s, sep='\n'): leastspaces = -1 Modified: py/trunk/py/doc/contact.txt ============================================================================== --- py/trunk/py/doc/contact.txt (original) +++ py/trunk/py/doc/contact.txt Mon Feb 12 18:58:38 2007 @@ -53,7 +53,7 @@ .. _`zope3`: http://zope3.zwiki.org/ .. _twisted: http://www.twistedmatrix.org -.. _future: future/future.html +.. _future: future.html .. _`get an account`: Copied: py/trunk/py/doc/future.txt (from r38620, py/trunk/py/doc/future/future.txt) ============================================================================== --- py/trunk/py/doc/future/future.txt (original) +++ py/trunk/py/doc/future.txt Mon Feb 12 18:58:38 2007 @@ -50,7 +50,7 @@ across the network. The programs who "own" these endpoints remain connected. -.. _`py.execnet`: ../execnet.html +.. _`py.execnet`: execnet.html Benchmarking and persistent storage ========================================= @@ -72,7 +72,7 @@ .. _`future book`: future.html .. _`PEP-324 subprocess module`: http://www.python.org/peps/pep-0324.html .. _`subprocess implementation`: http://www.lysator.liu.se/~astrand/popen5/ -.. _`py.test`: ../test.html +.. _`py.test`: test.html .. _`general-path`: Deleted: /py/trunk/py/doc/future/future.txt ============================================================================== --- /py/trunk/py/doc/future/future.txt Mon Feb 12 18:58:38 2007 +++ (empty file) @@ -1,119 +0,0 @@ -======================================================= -Visions and ideas for further development of the py lib -======================================================= - -.. contents:: -.. sectnum:: - -This document tries to describe directions and guiding ideas -for the near-future development of the py lib. *Note that all -statements within this document - even if they sound factual - -mostly just express thoughts and ideas. They not always refer to -real code so read with some caution.* - - -Distribute tests ad-hoc across multiple platforms -====================================================== - -After some more refactoring and unification of -the current testing and distribution support code -we'd like to be able to run tests on multiple -platforms simultanously and allow for interaction -and introspection into the (remote) failures. - - -Make APIGEN useful for more projects -================================================ - -The new APIGEN tool offers rich information -derived from running tests against an application: -argument types and callsites, i.e. it shows -the places where a particular API is used. -In its first incarnation, there are still -some specialties that likely prevent it -from documenting APIs for other projects. -We'd like to evolve to a `py.apigen` tool -that can make use of information provided -by a py.test run. - -Distribute channels/programs across networks -================================================ - -Apart from stabilizing setup/teardown procedures -for `py.execnet`_, we'd like to generalize its -implementation to allow connecting two programs -across multiple hosts, i.e. we'd like to arbitrarily -send "channels" across the network. Likely this -will be done by using the "pipe" model, i.e. -that each channel is actually a pair of endpoints, -both of which can be independently transported -across the network. The programs who "own" -these endpoints remain connected. - -.. _`py.execnet`: ../execnet.html - -Benchmarking and persistent storage -========================================= - -For storing test results, but also benchmarking -and other information, we need a solid way -to store all kinds of information from test runs. -We'd like to generate statistics or html-overview -out of it, but also use such information to determine when -a certain test broke, or when its performance -decreased considerably. - -.. _`CPython's distutils`: http://www.python.org/dev/doc/devel/lib/module-distutils.html - -.. _`restructured text`: http://docutils.sourceforge.net/docs/user/rst/quickref.html -.. _`python standard library`: http://www.python.org/doc/2.3.4/lib/lib.html -.. _`xpython EuroPython 2004 talk`: http://codespeak.net/svn/user/hpk/talks/xpython-talk.txt -.. _`under the xpy tree`: http://codespeak.net/svn/user/hpk/xpy/xml.py -.. _`future book`: future.html -.. _`PEP-324 subprocess module`: http://www.python.org/peps/pep-0324.html -.. _`subprocess implementation`: http://www.lysator.liu.se/~astrand/popen5/ -.. _`py.test`: ../test.html - - -.. _`general-path`: -.. _`a more general view on path objects`: - -Refactor path implementations to use a Filesystem Abstraction -============================================================= - -It seems like a good idea to refactor all python implementations to -use an internal Filesystem abstraction. The current code base -would be transformed to have Filesystem implementations for e.g. -local, subversion and subversion "working copy" filesystems. Today -the according code is scattered through path-handling code. - -On a related note, Armin Rigo has hacked `pylufs`_ and more recently has -written `pyfuse`_ which allow to -implement kernel-level linux filesystems with pure python. Now -the idea is that the mentioned filesystem implementations would -be directly usable for such linux-filesystem glue code. - -In other words, implementing a `memoryfs`_ or a `dictfs`_ would -give you two things for free: a filesystem mountable at kernel level -as well as a uniform "path" object allowing you to access your -filesystem in convenient ways. (At some point it might -even become interesting to think about interfacing to -`reiserfs v4 features`_ at the Filesystem level but that -is a can of subsequent worms). - -.. _`memoryfs`: http://codespeak.net/svn/user/arigo/hack/pyfuse/memoryfs.py -.. _`dictfs`: http://codespeak.net/pipermail/py-dev/2005-January/000191.html -.. _`pylufs`: http://codespeak.net/svn/user/arigo/hack/pylufs/ -.. _`pyfuse`: http://codespeak.net/svn/user/arigo/hack/pyfuse/ -.. _`reiserfs v4 features`: http://www.namesys.com/v4/v4.html - - - -Consider more features -================================== - -There are many more features and useful classes -that might be nice to integrate. For example, we might put -Armin's `lazy list`_ implementation into the py lib. - -.. _`lazy list`: http://codespeak.net/svn/user/arigo/hack/misc/collect.py Modified: py/trunk/py/doc/index.txt ============================================================================== --- py/trunk/py/doc/index.txt (original) +++ py/trunk/py/doc/index.txt Mon Feb 12 18:58:38 2007 @@ -55,6 +55,6 @@ .. _`py lib scripts`: bin.html .. _`py.xml`: xml.html .. _`Why What how py?`: why_py.html -.. _`future`: future/future.html +.. _`future`: future.html .. _`miscellaneous features`: misc.html Modified: py/trunk/py/doc/misc.txt ============================================================================== --- py/trunk/py/doc/misc.txt (original) +++ py/trunk/py/doc/misc.txt Mon Feb 12 18:58:38 2007 @@ -142,10 +142,10 @@ will have to install the `pywin32 package`_. -.. _`future book`: future/future.html +.. _`future book`: future.html .. _`PEP-324 subprocess module`: http://www.python.org/peps/pep-0324.html .. _`subprocess implementation`: http://www.lysator.liu.se/~astrand/popen5/ -.. _`a more general view on path objects`: future/future.html#general-path +.. _`a more general view on path objects`: future.html#general-path .. _`pywin32 package`: http://pywin32.sourceforge.net/ finding an executable local path Modified: py/trunk/py/doc/why_py.txt ============================================================================== --- py/trunk/py/doc/why_py.txt (original) +++ py/trunk/py/doc/why_py.txt Mon Feb 12 18:58:38 2007 @@ -181,7 +181,7 @@ .. _`py-dev mailing list`: http://codespeak.net/mailman/listinfo/py-dev .. _`test environment`: test.html .. _`PyPy`: http://codespeak.net/pypy -.. _future: future/future.html +.. _future: future.html .. _`py.test tool and library`: test.html .. _merlinux: http://merlinux.de From hpk at codespeak.net Mon Feb 12 19:11:08 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 12 Feb 2007 19:11:08 +0100 (CET) Subject: [py-svn] r38626 - py/trunk/py/apigen Message-ID: <20070212181108.8DEC61009A@code0.codespeak.net> Author: hpk Date: Mon Feb 12 19:11:07 2007 New Revision: 38626 Modified: py/trunk/py/apigen/todo.txt Log: some XXX to TODO, and adding a part about proper linking Modified: py/trunk/py/apigen/todo.txt ============================================================================== --- py/trunk/py/apigen/todo.txt (original) +++ py/trunk/py/apigen/todo.txt Mon Feb 12 19:11:07 2007 @@ -3,18 +3,18 @@ py/apigen sources [rev XXX] - DONE + DONE (XXX: i don't see it as done, are you sure? * and "namespace" pages: builtin namespace index [rev XXX] DONE, except they're now called 'index of [rev. XXX]' + (XXX: strange, i also don't see this, am i doing something wrong?) * get konqueror to display indents in source code better? (currently it doesn't look like more than a single space) - * function view: def __init__(self, rawcode): @@ -35,9 +35,12 @@ DONE too -* allow for flexibility regarding linking from - py/doc/*.txt documents to apigen with respect - to where apigen/ docs are located. +* linking from docs to apigen and back: - LATER, as discussed + XXX holger thinks that apigen needs a doc_relpath + (symettric to py/doc/conftest needing a apigen_relpath) + if you can't find a way to provide this as a command line param, + then we probably need to hardcode it. + note that both relpath's are related to how we map docs and + apigen into the URL namespace. From hpk at codespeak.net Mon Feb 12 21:17:38 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 12 Feb 2007 21:17:38 +0100 (CET) Subject: [py-svn] r38640 - py/dist Message-ID: <20070212201738.C3FE310093@code0.codespeak.net> Author: hpk Date: Mon Feb 12 21:17:37 2007 New Revision: 38640 Added: py/dist/ - copied from r38639, py/trunk/ Log: snapshotting trunk to dist From guido at codespeak.net Tue Feb 13 00:10:39 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Tue, 13 Feb 2007 00:10:39 +0100 (CET) Subject: [py-svn] r38649 - py/trunk/py/bin Message-ID: <20070212231039.3FC8D10094@code0.codespeak.net> Author: guido Date: Tue Feb 13 00:10:38 2007 New Revision: 38649 Modified: py/trunk/py/bin/_update_website.py Log: Adding _findpy call to not have to set PYTHONPATH to find the py lib. Modified: py/trunk/py/bin/_update_website.py ============================================================================== --- py/trunk/py/bin/_update_website.py (original) +++ py/trunk/py/bin/_update_website.py Tue Feb 13 00:10:38 2007 @@ -5,7 +5,7 @@ rsyncs the whole package (with all the ReST docs converted to HTML) as well as the apigen docs to a given remote host and path """ - +from _findpy import py import py import sys From guido at codespeak.net Tue Feb 13 00:14:51 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Tue, 13 Feb 2007 00:14:51 +0100 (CET) Subject: [py-svn] r38650 - in py/trunk/py: apigen bin Message-ID: <20070212231451.8D4351007B@code0.codespeak.net> Author: guido Date: Tue Feb 13 00:14:50 2007 New Revision: 38650 Added: py/trunk/py/bin/_docgen.py (contents, props changed) Modified: py/trunk/py/apigen/layout.py Log: Added env var for creating relative links to the py lib docs (in the nav bar), added script to generate py lib's docs and api docs in a subdir in one go. Modified: py/trunk/py/apigen/layout.py ============================================================================== --- py/trunk/py/apigen/layout.py (original) +++ py/trunk/py/apigen/layout.py Tue Feb 13 00:14:50 2007 @@ -31,12 +31,15 @@ self.body.insert(0, self.nav) def update_menubar_links(self, node): + docrelpath = py.std.os.environ.get('APIGEN_DOCRELPATH', '../py/doc') + if not docrelpath.endswith('/'): + docrelpath += '/' for item in node: if not isinstance(item, py.xml.Tag): continue if (item.__class__.__name__ == 'a' and hasattr(item.attr, 'href') and not item.attr.href.startswith('http://')): - item.attr.href = self.relpath + '../py/doc/' + item.attr.href + item.attr.href = self.relpath + docrelpath + item.attr.href def setup_scripts_styles(self, copyto=None): for path, name in self.stylesheets: Added: py/trunk/py/bin/_docgen.py ============================================================================== --- (empty file) +++ py/trunk/py/bin/_docgen.py Tue Feb 13 00:14:50 2007 @@ -0,0 +1,59 @@ +#!/usr/bin/env python + +""" quick tool to get documentation + apigen docs generated + + given a certain targetpath, apigen docs will be placed in 'apigen', + + user can choose to only build either docs or apigen docs: in this case, + the navigation bar will be adjusted +""" + +from _findpy import py +import py +pypath = py.__package__.getpath() + +def run_tests(path, envvars='', args=''): + pytestpath = pypath.join('bin/py.test') + cmd = ('PYTHONPATH="%s" %s python "%s" %s "%s"' % + (pypath.dirpath(), envvars, pytestpath, args, path)) + print cmd + py.process.cmdexec(cmd) + +def build_apigen_docs(targetpath, testargs=''): + run_tests(pypath, + 'APIGEN_TARGET="%s/apigen" APIGEN_DOCRELPATH="../doc/"' % ( + targetpath,), + testargs + ' --apigen="%s/apigen/apigen.py"' % (pypath,)) + +def build_docs(targetpath, testargs): + docpath = pypath.join('doc') + run_tests(docpath, '', + testargs + ' --forcegen --apigenrelpath="../apigen/"') + topath = targetpath.ensure('doc', dir=True) + docpath.copy(topath) + +def build_nav(targetpath, docs=True, api=True): + pass + +def build(targetpath, docs=True, api=True, testargs=''): + targetpath.ensure(dir=True) + if docs: + print 'building docs' + build_docs(targetpath, testargs) + if api: + print 'building api' + build_apigen_docs(targetpath, testargs) + +if __name__ == '__main__': + import sys + if len(sys.argv) == 1: + print 'usage: %s [options]' + print + print ' targetdir: a path to a directory (created if it doesn\'t' + print ' exist) where the docs are put' + print ' options: options passed to py.test when running the tests' + sys.exit(1) + targetpath = py.path.local(sys.argv[1]) + args = ' '.join(sys.argv[2:]) + build(targetpath, True, True, args) + From guido at codespeak.net Tue Feb 13 02:01:25 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Tue, 13 Feb 2007 02:01:25 +0100 (CET) Subject: [py-svn] r38652 - in py/trunk/py/apigen: . testing Message-ID: <20070213010125.1FC0A1009C@code0.codespeak.net> Author: guido Date: Tue Feb 13 02:01:23 2007 New Revision: 38652 Modified: py/trunk/py/apigen/html.py py/trunk/py/apigen/htmlgen.py py/trunk/py/apigen/style.css py/trunk/py/apigen/testing/test_htmlgen.py py/trunk/py/apigen/todo.txt Log: Fixed rendering problems in IE, updated todo.txt. Modified: py/trunk/py/apigen/html.py ============================================================================== --- py/trunk/py/apigen/html.py (original) +++ py/trunk/py/apigen/html.py Tue Feb 13 02:01:23 2007 @@ -134,22 +134,32 @@ super(H.PythonSource, self).__init__( H.div(*sourceels)) - class SourceBlock(html.div): - style = html.Style(margin_top='1em', margin_bottom='1em') + class SourceBlock(html.table): def __init__(self): - self.linenotable = lntable = H.table(style='float: left') + tbody = H.tbody() + row = H.tr() + tbody.append(row) + linenocell = H.td(style='width: 1%') + row.append(linenocell) + linecell = H.td() + row.append(linecell) + + self.linenotable = lntable = H.table() self.linenotbody = lntbody = H.tbody() lntable.append(lntbody) + linenocell.append(lntable) self.linetable = ltable = H.table() self.linetbody = ltbody = H.tbody() ltable.append(ltbody) - - super(H.SourceBlock, self).__init__(lntable, ltable) + linecell.append(ltable) + + super(H.SourceBlock, self).__init__(tbody, class_='codeblock') def add_line(self, lineno, els): self.linenotbody.append(H.tr(H.td(lineno, class_='lineno'))) - self.linetbody.append(H.tr(H.td(class_='code', *els))) + self.linetbody.append(H.tr(H.td(H.pre(class_='code', *els), + class_='codecell'))) class NonPythonSource(Content): def __init__(self, *args): Modified: py/trunk/py/apigen/htmlgen.py ============================================================================== --- py/trunk/py/apigen/htmlgen.py (original) +++ py/trunk/py/apigen/htmlgen.py Tue Feb 13 02:01:23 2007 @@ -247,7 +247,7 @@ enc = source_html.get_module_encoding(fspath.strpath) source = fspath.read() sep = get_linesep(source) - colored = enumerate_and_color(source.split(sep), 0, enc) + colored = [enumerate_and_color(source.split(sep), 0, enc)] tag = H.PythonSource(colored) nav = self.build_navigation(fspath) return tag, nav Modified: py/trunk/py/apigen/style.css ============================================================================== --- py/trunk/py/apigen/style.css (original) +++ py/trunk/py/apigen/style.css Tue Feb 13 02:01:23 2007 @@ -12,16 +12,25 @@ font-size: 0.9em; width: 155px; vertical-align: top; - margin-top: 0.5em; position: absolute; - position: fixed; top: 130px; left: 4px; bottom: 4px; overflow: auto; } -div.sidebar .selected a { +/* trick to not make IE ignore something (>) */ +body > .sidebar { + position: fixed; +} + +div.sidebar a, div.sidebar a:visited, div.sidebar a:hover { + color: blue; + text-decoration: none; +} + +div.sidebar .selected a, div.sidebar .selected a:visited, +div.sidebar .selected a:hover { color: white; background-color: #3ba6ec; } @@ -52,6 +61,11 @@ text-decoration: underline; } +.codeblock { + margin-top: 0.5em; + margin-bottom: 0.5em; +} + .code a { color: blue; font-weight: bold; @@ -69,14 +83,20 @@ border-right-width: 1px; } -.code { +.codecell { line-height: 1.4em; height: 1.4em; padding-left: 1em; - white-space: pre; +} + +pre.code { + line-height: 1.3em; + height: 1.3em; + background-color: white; + margin: 0px; + padding: 0px; + border: 0px; font-family: monospace, Monaco; - margin-top: 0.5em; - margin-bottom: 0.5em; } .comment { Modified: py/trunk/py/apigen/testing/test_htmlgen.py ============================================================================== --- py/trunk/py/apigen/testing/test_htmlgen.py (original) +++ py/trunk/py/apigen/testing/test_htmlgen.py Tue Feb 13 02:01:23 2007 @@ -65,56 +65,86 @@ def test_enumerate_and_color(): colored = htmlgen.enumerate_and_color(['def foo():', ' print "bar"'], 0, 'ascii') - div = py.xml.html.div(*colored).unicode(indent=0) + div = py.xml.html.div(colored).unicode(indent=0) print repr(div) - assert_eq_string(div, + assert_eq_string(div, u'
' - '
1
' + '
' + '' + '' + '' + '' + '' + '' + '
' + '' '' '' '' '' '
1
2
' + '
' '' '' - '' - '' '' '
' + '
' + '
'
                     'def foo():'
+                    '
' '
' + '
' + '
'
                     '  print'
                     ' "bar"'
+                    '
' '
' + '
' '
') def test_enumerate_and_color_multiline(): colored = htmlgen.enumerate_and_color(['code = """\\', 'foo bar', '"""'], 0, 'ascii') - div = py.xml.html.div(*colored).unicode(indent=0) + div = py.xml.html.div(colored).unicode(indent=0) print repr(div) - assert_eq_string (div, + assert_eq_string (div, u'
' - '' + '
' + '' + '' + '' + '' + '' + '' + '
' + '' '' '' '' '' '' '
1
2
3
' + '
' '' '' - '' - '' - '' '' '
' + '
' + '
'
                     'code = """\\'
+                    '
' '
' + '
' + '
'
                     'foo bar'
+                    '
' '
' + '
' + '
'
                     '"""'
+                    '
' '
' + '
' '
') def test_show_property(): Modified: py/trunk/py/apigen/todo.txt ============================================================================== --- py/trunk/py/apigen/todo.txt (original) +++ py/trunk/py/apigen/todo.txt Tue Feb 13 02:01:23 2007 @@ -15,6 +15,12 @@ * get konqueror to display indents in source code better? (currently it doesn't look like more than a single space) + Hrmph, fonts look just fine to me :( what machine is this (new laptop btw?)? + you seem to have a font problem... If you really want me to fix it for your + machine, please give me access... + + I also made sure IE looks (somewhat) good... + * function view: def __init__(self, rawcode): @@ -44,3 +50,12 @@ note that both relpath's are related to how we map docs and apigen into the URL namespace. + Currently handled by using an env var (APIGEN_DOCRELPATH), since + to make it possible to run py.test --apigen on the full py lib _and_ + set the option, it would have to be global (yuck), and apigen used + an env var already anyway... Of course it can easily be changed to an + option if you like that better... + + There's now also a script bin/_docgen.py that runs all the tests + and builds the py lib docs + api ones in one go. + From guido at codespeak.net Tue Feb 13 02:11:30 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Tue, 13 Feb 2007 02:11:30 +0100 (CET) Subject: [py-svn] r38653 - py/trunk/py/apigen/testing Message-ID: <20070213011130.D5D1010072@code0.codespeak.net> Author: guido Date: Tue Feb 13 02:11:30 2007 New Revision: 38653 Modified: py/trunk/py/apigen/testing/test_apigen_example.py Log: Skipping some tests on win32, for some reason svnwc.info().created_rev seems to fail (need to look into it in more detail later). Modified: py/trunk/py/apigen/testing/test_apigen_example.py ============================================================================== --- py/trunk/py/apigen/testing/test_apigen_example.py (original) +++ py/trunk/py/apigen/testing/test_apigen_example.py Tue Feb 13 02:11:30 2007 @@ -320,6 +320,9 @@ _checkhtml(html) def test_get_revision(self): + if py.std.sys.platform.startswith('win'): + py.test.skip('broken on win32 for some reason (svn caching?), ' + 'skipping') # XXX a lot of setup required for this one... more like a functional # test I fear @@ -438,6 +441,9 @@ ]) def test_get_revision(self): + if py.std.sys.platform.startswith('win'): + py.test.skip('broken on win32 for some reason (svn caching?), ' + 'skipping') repo = make_test_repo('test_get_revision_source_repo') wc = py.path.svnwc(py.test.ensuretemp('test_get_revision_source_wc')) wc.checkout(repo.url) From hpk at codespeak.net Tue Feb 13 09:18:35 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 13 Feb 2007 09:18:35 +0100 (CET) Subject: [py-svn] r38659 - py/trunk/py/doc Message-ID: <20070213081835.72609100AB@code0.codespeak.net> Author: hpk Date: Tue Feb 13 09:18:31 2007 New Revision: 38659 Modified: py/trunk/py/doc/release-0.9.0.txt Log: mention that we think that py lib works on 2.3, 2.4 and 2.5 Modified: py/trunk/py/doc/release-0.9.0.txt ============================================================================== --- py/trunk/py/doc/release-0.9.0.txt (original) +++ py/trunk/py/doc/release-0.9.0.txt Tue Feb 13 09:18:31 2007 @@ -11,7 +11,7 @@ * py.magic.greenlet: micro-threads on standard CPython ("stackless-light") * py.path: path abstractions over local and subversion files * rich documentation of py's exported API -* tested against Win32, Linux and OSX +* tested against Win32, Linux and OSX, python 2.3-2.5 All these features and their API have extensive documentation, generated with the new "apigen", which we intend to make accessible From fijal at codespeak.net Tue Feb 13 13:33:46 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 13 Feb 2007 13:33:46 +0100 (CET) Subject: [py-svn] r38683 - py/trunk/py/doc Message-ID: <20070213123346.66A80100AD@code0.codespeak.net> Author: fijal Date: Tue Feb 13 13:33:45 2007 New Revision: 38683 Modified: py/trunk/py/doc/future.txt Log: Add some information about pdb + apigen Modified: py/trunk/py/doc/future.txt ============================================================================== --- py/trunk/py/doc/future.txt (original) +++ py/trunk/py/doc/future.txt Tue Feb 13 13:33:45 2007 @@ -36,6 +36,16 @@ that can make use of information provided by a py.test run. +Consider APIGEN and pdb integration +=================================== + +Apigen is a source of a great information about functions, +methods and objects. Pdb is a nice tool for debugging. +While not get them together? Imagine the world where you can +ask "info function\_name" and you'll get all the information +regarding types coming, types getting out, possible exceptions, +field types and call sites. + Distribute channels/programs across networks ================================================ From guido at codespeak.net Tue Feb 13 13:49:38 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Tue, 13 Feb 2007 13:49:38 +0100 (CET) Subject: [py-svn] r38685 - py/trunk/py/doc Message-ID: <20070213124938.108E9100B2@code0.codespeak.net> Author: guido Date: Tue Feb 13 13:49:37 2007 New Revision: 38685 Modified: py/trunk/py/doc/future.txt Log: Partial rewrite of fijal's pdb/apigen text. Modified: py/trunk/py/doc/future.txt ============================================================================== --- py/trunk/py/doc/future.txt (original) +++ py/trunk/py/doc/future.txt Tue Feb 13 13:49:37 2007 @@ -39,12 +39,13 @@ Consider APIGEN and pdb integration =================================== -Apigen is a source of a great information about functions, -methods and objects. Pdb is a nice tool for debugging. -While not get them together? Imagine the world where you can -ask "info function\_name" and you'll get all the information -regarding types coming, types getting out, possible exceptions, -field types and call sites. +The information provided by APIGEN can be used in many +different ways. An example of this could be to write +an extension to pdb which makes it available. +Imagine you could issue a pdb command +"info " and get information +regarding incoming, and outgoing types, possible +exceptions, field types and call sites. Distribute channels/programs across networks ================================================ From guido at codespeak.net Tue Feb 13 15:26:48 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Tue, 13 Feb 2007 15:26:48 +0100 (CET) Subject: [py-svn] r38700 - py/trunk/py/bin Message-ID: <20070213142648.98FCB100C0@code0.codespeak.net> Author: guido Date: Tue Feb 13 15:26:46 2007 New Revision: 38700 Modified: py/trunk/py/bin/_docgen.py Log: Fixed paths, docs are now no longer placed in a sub dir. Modified: py/trunk/py/bin/_docgen.py ============================================================================== --- py/trunk/py/bin/_docgen.py (original) +++ py/trunk/py/bin/_docgen.py Tue Feb 13 15:26:46 2007 @@ -21,16 +21,15 @@ def build_apigen_docs(targetpath, testargs=''): run_tests(pypath, - 'APIGEN_TARGET="%s/apigen" APIGEN_DOCRELPATH="../doc/"' % ( + 'APIGEN_TARGET="%s/apigen" APIGEN_DOCRELPATH="../"' % ( targetpath,), testargs + ' --apigen="%s/apigen/apigen.py"' % (pypath,)) def build_docs(targetpath, testargs): docpath = pypath.join('doc') run_tests(docpath, '', - testargs + ' --forcegen --apigenrelpath="../apigen/"') - topath = targetpath.ensure('doc', dir=True) - docpath.copy(topath) + testargs + ' --forcegen --apigenrelpath="apigen/"') + docpath.copy(targetpath) def build_nav(targetpath, docs=True, api=True): pass From guido at codespeak.net Tue Feb 13 15:29:13 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Tue, 13 Feb 2007 15:29:13 +0100 (CET) Subject: [py-svn] r38701 - py/trunk/py/apigen Message-ID: <20070213142913.5DE97100C2@code0.codespeak.net> Author: guido Date: Tue Feb 13 15:29:12 2007 New Revision: 38701 Modified: py/trunk/py/apigen/style.css Log: Fixed line heights in Konqueror. Modified: py/trunk/py/apigen/style.css ============================================================================== --- py/trunk/py/apigen/style.css (original) +++ py/trunk/py/apigen/style.css Tue Feb 13 15:29:12 2007 @@ -73,9 +73,10 @@ } .lineno { - line-height: 1.4em; height: 1.4em; text-align: right; + padding: 0px; + margin: 0px; color: #555; width: 3em; padding-right: 1em; @@ -84,14 +85,14 @@ } .codecell { - line-height: 1.4em; height: 1.4em; + padding: 0px; + margin: 0px; padding-left: 1em; } pre.code { line-height: 1.3em; - height: 1.3em; background-color: white; margin: 0px; padding: 0px; From hpk at codespeak.net Tue Feb 13 16:05:28 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 13 Feb 2007 16:05:28 +0100 (CET) Subject: [py-svn] r38706 - in py/trunk/py/test: . terminal Message-ID: <20070213150528.7FDFE10089@code0.codespeak.net> Author: hpk Date: Tue Feb 13 16:05:21 2007 New Revision: 38706 Modified: py/trunk/py/test/config.py py/trunk/py/test/terminal/terminal.py Log: privatizing config.conftest to config._conftest as well Modified: py/trunk/py/test/config.py ============================================================================== --- py/trunk/py/test/config.py (original) +++ py/trunk/py/test/config.py Tue Feb 13 16:05:21 2007 @@ -32,7 +32,7 @@ self.option = CmdOptions() self._parser = optparse.OptionParser( usage="usage: %prog [options] [query] [filenames of tests]") - self.conftest = Conftest() + self._conftest = Conftest() self._initialized = False def parse(self, args): @@ -43,7 +43,7 @@ "can only parse cmdline args once per Config object") self._initialized = True adddefaultoptions(self) - self.conftest.setinitial(args) + self._conftest.setinitial(args) args = [str(x) for x in args] cmdlineoption, args = self._parser.parse_args(args) self.option.__dict__.update(vars(cmdlineoption)) @@ -80,7 +80,7 @@ pkgpath = path.pypkgpath() if pkgpath is None: pkgpath = path.check(file=1) and path.dirpath() or path - col = self.conftest.rget("Directory", pkgpath)(pkgpath) + col = self._conftest.rget("Directory", pkgpath)(pkgpath) col._config = self return col @@ -94,7 +94,7 @@ return getattr(self.option, name) except AttributeError: try: - mod, relroots = self.conftest.rget_with_confmod(name, path) + mod, relroots = self._conftest.rget_with_confmod(name, path) except KeyError: return None modpath = py.path.local(mod.__file__).dirpath() @@ -132,7 +132,7 @@ try: return getattr(self.option, name) except AttributeError: - return self.conftest.rget(name, path) + return self._conftest.rget(name, path) def initsession(self): """ return an initialized session object. """ @@ -146,11 +146,11 @@ and looked up in initial config modules. """ if self.option.session is not None: - return self.conftest.rget(self.option.session) + return self._conftest.rget(self.option.session) else: name = self._getsessionname() try: - return self.conftest.rget(name) + return self._conftest.rget(name) except KeyError: pass importpath = globals()[name] @@ -229,8 +229,8 @@ self.__file__ = "" args, conftestdict, cmdlineopts = repr self.args = [self.topdir.join(x) for x in args] - self.conftest.setinitial(self.args) - self.conftest._path2confmods[None].append(override(conftestdict)) + self._conftest.setinitial(self.args) + self._conftest._path2confmods[None].append(override(conftestdict)) for name, val in cmdlineopts.items(): setattr(self.option, name, val) Modified: py/trunk/py/test/terminal/terminal.py ============================================================================== --- py/trunk/py/test/terminal/terminal.py (original) +++ py/trunk/py/test/terminal/terminal.py Tue Feb 13 16:05:21 2007 @@ -138,7 +138,7 @@ for x in colitems: self.out.line("test target: %s" %(x.fspath,)) - conftestmodules = self.config.conftest.getconftestmodules(None) + conftestmodules = self.config._conftest.getconftestmodules(None) for i,x in py.builtin.enumerate(conftestmodules): self.out.line("initial conf %d: %s" %(i, x.__file__)) From fijal at codespeak.net Tue Feb 13 16:15:28 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 13 Feb 2007 16:15:28 +0100 (CET) Subject: [py-svn] r38708 - in py/trunk/py/test/rsession: . testing Message-ID: <20070213151528.57E1E100A6@code0.codespeak.net> Author: fijal Date: Tue Feb 13 16:15:26 2007 New Revision: 38708 Modified: py/trunk/py/test/rsession/testing/test_webjs.py py/trunk/py/test/rsession/webjs.py Log: document.location is not in DOM. We need to implemented some stuff in js backend to be able to test this Modified: py/trunk/py/test/rsession/testing/test_webjs.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_webjs.py (original) +++ py/trunk/py/test/rsession/testing/test_webjs.py Tue Feb 13 16:15:26 2007 @@ -43,6 +43,7 @@ assert str(body.childNodes[1].nodeName) == 'A' def test_set_msgbox(): + py.test.skip msgbox = dom.window.document.getElementById('messagebox') assert len(msgbox.childNodes) == 0 webjs.set_msgbox('foo', 'bar') Modified: py/trunk/py/test/rsession/webjs.py ============================================================================== --- py/trunk/py/test/rsession/webjs.py (original) +++ py/trunk/py/test/rsession/webjs.py Tue Feb 13 16:15:26 2007 @@ -249,7 +249,7 @@ txt = create_text_elem(item_name + "\n" + data) pre.appendChild(txt) msgbox.appendChild(pre) - dom.document.location = "#message" + dom.window.location.assign("#message") glob.data_empty = False def show_traceback(item_name="aa"): From fijal at codespeak.net Tue Feb 13 16:24:11 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 13 Feb 2007 16:24:11 +0100 (CET) Subject: [py-svn] r38709 - py/trunk/py/test/rsession/testing Message-ID: <20070213152411.05EDB100AD@code0.codespeak.net> Author: fijal Date: Tue Feb 13 16:24:08 2007 New Revision: 38709 Modified: py/trunk/py/test/rsession/testing/test_boxing.py Log: kill dead code Modified: py/trunk/py/test/rsession/testing/test_boxing.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_boxing.py (original) +++ py/trunk/py/test/rsession/testing/test_boxing.py Tue Feb 13 16:24:08 2007 @@ -11,8 +11,7 @@ from py.__.test.rsession.testing import example2 def setup_module(mod): - mod.rootdir = py.path.local(py.__file__).dirpath().dirpath() - mod.config = py.test.config._reparse([mod.rootdir]) + mod.config = py.test.config._reparse([]) def test_basic_boxing(): # XXX: because we do not have option transfer From fijal at codespeak.net Tue Feb 13 16:24:34 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 13 Feb 2007 16:24:34 +0100 (CET) Subject: [py-svn] r38710 - py/trunk/py/test/rsession/testing Message-ID: <20070213152434.E3126100B4@code0.codespeak.net> Author: fijal Date: Tue Feb 13 16:24:33 2007 New Revision: 38710 Modified: py/trunk/py/test/rsession/testing/test_webjs.py Log: Invoke the skip rather than have it Modified: py/trunk/py/test/rsession/testing/test_webjs.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_webjs.py (original) +++ py/trunk/py/test/rsession/testing/test_webjs.py Tue Feb 13 16:24:33 2007 @@ -43,7 +43,7 @@ assert str(body.childNodes[1].nodeName) == 'A' def test_set_msgbox(): - py.test.skip + py.test.skip("not implemented in genjs") msgbox = dom.window.document.getElementById('messagebox') assert len(msgbox.childNodes) == 0 webjs.set_msgbox('foo', 'bar') From fijal at codespeak.net Tue Feb 13 16:31:33 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 13 Feb 2007 16:31:33 +0100 (CET) Subject: [py-svn] r38711 - py/trunk/py/test/rsession/testing Message-ID: <20070213153133.639C8100AD@code0.codespeak.net> Author: fijal Date: Tue Feb 13 16:31:31 2007 New Revision: 38711 Modified: py/trunk/py/test/rsession/testing/test_rsession.py Log: Avoid infinte recursion when rsyncing (well finite - OSError file too long) Modified: py/trunk/py/test/rsession/testing/test_rsession.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_rsession.py (original) +++ py/trunk/py/test/rsession/testing/test_rsession.py Tue Feb 13 16:31:31 2007 @@ -182,7 +182,7 @@ """ allevents = [] hosts = [HostInfo('localhost:%s' % self.dest)] - tmpdir = py.test.ensuretemp("nice") + tmpdir = self.source tmpdir.ensure("__init__.py") tmpdir.ensure("conftest.py").write(py.code.Source(""" dist_hosts = ['localhost:%s'] From fijal at codespeak.net Tue Feb 13 16:32:52 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 13 Feb 2007 16:32:52 +0100 (CET) Subject: [py-svn] r38712 - py/trunk/py/test/rsession/testing Message-ID: <20070213153252.35650100AD@code0.codespeak.net> Author: fijal Date: Tue Feb 13 16:32:49 2007 New Revision: 38712 Modified: py/trunk/py/test/rsession/testing/test_boxing.py Log: Avoid py.test.config._reparse([]) Modified: py/trunk/py/test/rsession/testing/test_boxing.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_boxing.py (original) +++ py/trunk/py/test/rsession/testing/test_boxing.py Tue Feb 13 16:32:49 2007 @@ -11,7 +11,8 @@ from py.__.test.rsession.testing import example2 def setup_module(mod): - mod.config = py.test.config._reparse([]) + tmpdir = py.test.ensuretemp("boxtests") + mod.config = py.test.config._reparse([tmpdir]) def test_basic_boxing(): # XXX: because we do not have option transfer From fijal at codespeak.net Tue Feb 13 16:34:18 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 13 Feb 2007 16:34:18 +0100 (CET) Subject: [py-svn] r38713 - py/trunk/py/test/rsession/testing Message-ID: <20070213153418.629F6100AD@code0.codespeak.net> Author: fijal Date: Tue Feb 13 16:34:16 2007 New Revision: 38713 Modified: py/trunk/py/test/rsession/testing/test_rsession.py Log: Use self.source wherever applicable Modified: py/trunk/py/test/rsession/testing/test_rsession.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_rsession.py (original) +++ py/trunk/py/test/rsession/testing/test_rsession.py Tue Feb 13 16:34:16 2007 @@ -62,7 +62,7 @@ def test_distribution_rsync_roots_example(self): destdir = py.test.ensuretemp("example_dist_destdir") subdir = "sub_example_dist" - tmpdir = py.test.ensuretemp("example_distribution") + tmpdir = self.source tmpdir.ensure(subdir, "conftest.py").write(py.code.Source(""" dist_hosts = ["localhost:%s"] dist_rsync_roots = ["%s", "../py"] @@ -118,7 +118,7 @@ hosts = [HostInfo('localhost:%s' % self.dest)] setup_events = [] teardown_events = [] - tmpdir = py.test.ensuretemp("emptyconftest") + tmpdir = self.source config = py.test.config._reparse([tmpdir]) hm = HostManager(config, hosts) nodes = hm.setup_hosts(setup_events.append) From fijal at codespeak.net Tue Feb 13 17:37:28 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 13 Feb 2007 17:37:28 +0100 (CET) Subject: [py-svn] r38726 - py/trunk/py/test/rsession Message-ID: <20070213163728.5FAAC100B5@code0.codespeak.net> Author: fijal Date: Tue Feb 13 17:37:26 2007 New Revision: 38726 Modified: py/trunk/py/test/rsession/web.py Log: shuffle -> sample Modified: py/trunk/py/test/rsession/web.py ============================================================================== --- py/trunk/py/test/rsession/web.py (original) +++ py/trunk/py/test/rsession/web.py Tue Feb 13 17:37:26 2007 @@ -178,9 +178,8 @@ self._sesslock.acquire() try: while 1: - chars = list(py.std.string.lowercase) - py.std.random.shuffle(chars) - sessid = ''.join(chars[:8]) + sessid = ''.join(py.std.random.sample( + py.std.string.lowercase, 8)) if sessid not in self._sessids: self._sessids.append(sessid) break From hpk at codespeak.net Tue Feb 13 18:06:03 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 13 Feb 2007 18:06:03 +0100 (CET) Subject: [py-svn] r38732 - py/dist Message-ID: <20070213170603.1028510089@code0.codespeak.net> Author: hpk Date: Tue Feb 13 18:06:03 2007 New Revision: 38732 Added: py/dist/ - copied from r38731, py/trunk/ Log: new snapshot of trunk to dist From hpk at codespeak.net Tue Feb 13 19:19:01 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 13 Feb 2007 19:19:01 +0100 (CET) Subject: [py-svn] r38739 - py/dist/py/doc Message-ID: <20070213181901.DDB82100AF@code0.codespeak.net> Author: hpk Date: Tue Feb 13 19:18:57 2007 New Revision: 38739 Modified: py/dist/py/doc/TODO.txt py/dist/py/doc/download.txt py/dist/py/doc/release-0.9.0.txt Log: update TODO, release announcement and download docs Modified: py/dist/py/doc/TODO.txt ============================================================================== --- py/dist/py/doc/TODO.txt (original) +++ py/dist/py/doc/TODO.txt Tue Feb 13 19:18:57 2007 @@ -1,46 +1,8 @@ Things to do for 0.9.0 ========================= - - -py/bin ------------ - -* review py/bin scripts abit - py.test - py.rest - py.lookup - py.cleanup - py.countloc - -review all py lib documentation -------------------------------------- - -* (hpk, in-progress) rename py/documentation to py/doc - (check web page and pypy usage of it) - -streamline exported API -------------------------------------- - -* (DONE) move not-to-be-exported Gateway() methods to _ - methods. - -* docstrings for all exported API - -* (DONE) remove: test.compat.TestCAse - -* check and likely remove already deprecated API - -* remove from public namespace: - XXX consider py.magic. invoke/revoke/patch/revert - (DONE) remove py.path.extpy - -* make "_" namespace: - py.log -> py._log (pypy!) - -* (done mostly) review py.io and write py.io.dupfile docstring - -* re-consider what to do with read and write methods of py.path classes (since - there are places that check for file-ness by doing hasattr(... 'write')) +* (XXX not done, but the documentation is marked accordingly) + make "_" namespace: py.log -> py._log (pypy!) packaging ------------------------------------- @@ -65,44 +27,11 @@ APIGEN / source viewer ------------------------------------- -* (DONE) make py.test --apigen=PATH_TO_SCRIPT - collect tracing information and call the apigen - script to produce api and source code documentation - -* deploy the above "py.test --apigen" run on codespeak - regularly, determine directory locations and URL namespace design. - * (DONE, XXX functions/methods?) integrate rest directive into py/documentation/conftest.py with help code from py.__.rest.directive.... make sure that the txt files in py/documentation/ use it -* (DONE) private py.test not-meant-to-be-public API: - here is a rough list what we want public on collectors: - - py.test.collect.Collector. - startcapture() - finishcapture() - setup() - teardown() - listchain() - listnames() - run() - join() - multijoin() - name, parent, fspath - * all collector class properties * - - and on py.test.Function|Item (which also has the collector interface): - execute() - - DONE move all Outcome (Skipped/Passed/...) classes to - a global place (outcome.py?) - - DONE all other attributes of collectors shall become private - -* after the above API cleanup there might be more :) - testing ----------- @@ -115,50 +44,10 @@ (guido tested all on win32, everything works except --dist (requires os.fork to work)) -* (DONE) see why startcapture() used to not use FD-based - "py.io.StdCaptureFD" to isolate standard output. - use that check if all py and PyPy tests pass - as good as they do without. - -* (DONE, except py.path.svn) make --box run on the trunk. - This requires having some sort of - is_boxed() function, while having it on config object seems - to be not that smart idea. - -* (DONE more or less) try to be as 2.2 compatible as possible - (use e.g. py.builtin.enumerate instead of "enumerate" directly) - -* (DONE) have all sessions check their options via - Session.fixoptions() and have session-particular tests and checks - accordingly. - distributed testing / RSession ------------------------------------ -* (DONE, except apigen) cleanup initialisation of config / get rid of pkgdir * (optional) see if more of py/test/session.py's Session can be reused -* (DONE, but slightly different way) - have dist_rsyncroots be relative to the conftest.py file - so that projects can define it for themselves, e.g. - pypy/conftest.py would contain:: - - dist_rsyncroots = ['../pypy', '../py'] - - for this there probably needs to be a:: - - config.getvalue_and_conftestpath() - - method with tests and documentation, and with providing - the right example. - - way it's done: - - dist_rsyncroots refer *only* to a directory where it's placed. - This avoids tons of confusion, like what to do if different conftests - point to the same place. So by now pypy rootdir should contain - conftest.py with:: - - dist_rsyncroots = ['pypy', 'pylib', 'lib-python'] code quality ----------------- @@ -177,7 +66,7 @@ * (needs review) adjust py.test documentation to reflect new collector/session architecture -* (in-progress) document py.test's conftest.py approach +* (in-progress, NOT DONE) document py.test's conftest.py approach * (postponed, likely) py.test fails to parse strangely formatted code after assertion failure Modified: py/dist/py/doc/download.txt ============================================================================== --- py/dist/py/doc/download.txt (original) +++ py/dist/py/doc/download.txt Tue Feb 13 19:18:57 2007 @@ -26,7 +26,7 @@ svn co http://codespeak.net/svn/py/release/0.9.x py-0.9.x -to obtain the complete code and documentation tree. +to obtain the complete code and documentation source. If you experience problems with the subversion checkout e.g. because you have a http-proxy in between that doesn't proxy Modified: py/dist/py/doc/release-0.9.0.txt ============================================================================== --- py/dist/py/doc/release-0.9.0.txt (original) +++ py/dist/py/doc/release-0.9.0.txt Tue Feb 13 19:18:57 2007 @@ -17,16 +17,12 @@ generated with the new "apigen", which we intend to make accessible for other python projects as well. -Here is the entry point for installing the py lib: - - http://codespeak.net/py/XXX # figure out exact scheme - -and here is the main entry point into the documentation: - - http://codespeak.net/py/XXX # figure out exact scheme +Download/Install: http://codespeak.net/py/0.9.0/download.html +Documentation/API: http://codespeak.net/py/0.9.0/index.html Work on the py lib has been partially funded by the -European Union and http://merlinux.de within the PyPy project. +European Union IST programme and by http://merlinux.de +within the PyPy project. best, have fun and let us know what you think! From hpk at codespeak.net Tue Feb 13 19:22:47 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 13 Feb 2007 19:22:47 +0100 (CET) Subject: [py-svn] r38741 - py/trunk/py/doc Message-ID: <20070213182247.24A57100AC@code0.codespeak.net> Author: hpk Date: Tue Feb 13 19:22:47 2007 New Revision: 38741 Modified: py/trunk/py/doc/TODO.txt py/trunk/py/doc/download.txt py/trunk/py/doc/release-0.9.0.txt Log: doing r38739 in the right place (dist will get recopied from trunk) Modified: py/trunk/py/doc/TODO.txt ============================================================================== --- py/trunk/py/doc/TODO.txt (original) +++ py/trunk/py/doc/TODO.txt Tue Feb 13 19:22:47 2007 @@ -1,46 +1,8 @@ Things to do for 0.9.0 ========================= - - -py/bin ------------ - -* review py/bin scripts abit - py.test - py.rest - py.lookup - py.cleanup - py.countloc - -review all py lib documentation -------------------------------------- - -* (hpk, in-progress) rename py/documentation to py/doc - (check web page and pypy usage of it) - -streamline exported API -------------------------------------- - -* (DONE) move not-to-be-exported Gateway() methods to _ - methods. - -* docstrings for all exported API - -* (DONE) remove: test.compat.TestCAse - -* check and likely remove already deprecated API - -* remove from public namespace: - XXX consider py.magic. invoke/revoke/patch/revert - (DONE) remove py.path.extpy - -* make "_" namespace: - py.log -> py._log (pypy!) - -* (done mostly) review py.io and write py.io.dupfile docstring - -* re-consider what to do with read and write methods of py.path classes (since - there are places that check for file-ness by doing hasattr(... 'write')) +* (XXX not done, but the documentation is marked accordingly) + make "_" namespace: py.log -> py._log (pypy!) packaging ------------------------------------- @@ -65,44 +27,11 @@ APIGEN / source viewer ------------------------------------- -* (DONE) make py.test --apigen=PATH_TO_SCRIPT - collect tracing information and call the apigen - script to produce api and source code documentation - -* deploy the above "py.test --apigen" run on codespeak - regularly, determine directory locations and URL namespace design. - * (DONE, XXX functions/methods?) integrate rest directive into py/documentation/conftest.py with help code from py.__.rest.directive.... make sure that the txt files in py/documentation/ use it -* (DONE) private py.test not-meant-to-be-public API: - here is a rough list what we want public on collectors: - - py.test.collect.Collector. - startcapture() - finishcapture() - setup() - teardown() - listchain() - listnames() - run() - join() - multijoin() - name, parent, fspath - * all collector class properties * - - and on py.test.Function|Item (which also has the collector interface): - execute() - - DONE move all Outcome (Skipped/Passed/...) classes to - a global place (outcome.py?) - - DONE all other attributes of collectors shall become private - -* after the above API cleanup there might be more :) - testing ----------- @@ -115,50 +44,10 @@ (guido tested all on win32, everything works except --dist (requires os.fork to work)) -* (DONE) see why startcapture() used to not use FD-based - "py.io.StdCaptureFD" to isolate standard output. - use that check if all py and PyPy tests pass - as good as they do without. - -* (DONE, except py.path.svn) make --box run on the trunk. - This requires having some sort of - is_boxed() function, while having it on config object seems - to be not that smart idea. - -* (DONE more or less) try to be as 2.2 compatible as possible - (use e.g. py.builtin.enumerate instead of "enumerate" directly) - -* (DONE) have all sessions check their options via - Session.fixoptions() and have session-particular tests and checks - accordingly. - distributed testing / RSession ------------------------------------ -* (DONE, except apigen) cleanup initialisation of config / get rid of pkgdir * (optional) see if more of py/test/session.py's Session can be reused -* (DONE, but slightly different way) - have dist_rsyncroots be relative to the conftest.py file - so that projects can define it for themselves, e.g. - pypy/conftest.py would contain:: - - dist_rsyncroots = ['../pypy', '../py'] - - for this there probably needs to be a:: - - config.getvalue_and_conftestpath() - - method with tests and documentation, and with providing - the right example. - - way it's done: - - dist_rsyncroots refer *only* to a directory where it's placed. - This avoids tons of confusion, like what to do if different conftests - point to the same place. So by now pypy rootdir should contain - conftest.py with:: - - dist_rsyncroots = ['pypy', 'pylib', 'lib-python'] code quality ----------------- @@ -177,7 +66,7 @@ * (needs review) adjust py.test documentation to reflect new collector/session architecture -* (in-progress) document py.test's conftest.py approach +* (in-progress, NOT DONE) document py.test's conftest.py approach * (postponed, likely) py.test fails to parse strangely formatted code after assertion failure Modified: py/trunk/py/doc/download.txt ============================================================================== --- py/trunk/py/doc/download.txt (original) +++ py/trunk/py/doc/download.txt Tue Feb 13 19:22:47 2007 @@ -26,7 +26,7 @@ svn co http://codespeak.net/svn/py/release/0.9.x py-0.9.x -to obtain the complete code and documentation tree. +to obtain the complete code and documentation source. If you experience problems with the subversion checkout e.g. because you have a http-proxy in between that doesn't proxy Modified: py/trunk/py/doc/release-0.9.0.txt ============================================================================== --- py/trunk/py/doc/release-0.9.0.txt (original) +++ py/trunk/py/doc/release-0.9.0.txt Tue Feb 13 19:22:47 2007 @@ -17,16 +17,12 @@ generated with the new "apigen", which we intend to make accessible for other python projects as well. -Here is the entry point for installing the py lib: - - http://codespeak.net/py/XXX # figure out exact scheme - -and here is the main entry point into the documentation: - - http://codespeak.net/py/XXX # figure out exact scheme +Download/Install: http://codespeak.net/py/0.9.0/download.html +Documentation/API: http://codespeak.net/py/0.9.0/index.html Work on the py lib has been partially funded by the -European Union and http://merlinux.de within the PyPy project. +European Union IST programme and by http://merlinux.de +within the PyPy project. best, have fun and let us know what you think! From hpk at codespeak.net Tue Feb 13 19:24:52 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 13 Feb 2007 19:24:52 +0100 (CET) Subject: [py-svn] r38742 - py/release Message-ID: <20070213182452.137B010071@code0.codespeak.net> Author: hpk Date: Tue Feb 13 19:24:52 2007 New Revision: 38742 Added: py/release/ Log: opening the directory for releases (and release branches) From hpk at codespeak.net Tue Feb 13 20:01:03 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 13 Feb 2007 20:01:03 +0100 (CET) Subject: [py-svn] r38743 - py/trunk/py/doc Message-ID: <20070213190103.81A481007B@code0.codespeak.net> Author: hpk Date: Tue Feb 13 20:01:02 2007 New Revision: 38743 Removed: py/trunk/py/doc/test-distributed.txt Modified: py/trunk/py/doc/apigen.txt py/trunk/py/doc/greenlet.txt Log: fix and remove unneeded external references remote test-distributed.txt which had lots of not actual links and IMO would need a higher-level introduction to become understandable. Modified: py/trunk/py/doc/apigen.txt ============================================================================== --- py/trunk/py/doc/apigen.txt (original) +++ py/trunk/py/doc/apigen.txt Tue Feb 13 20:01:02 2007 @@ -282,7 +282,3 @@ For more information, questions, remarks, etc. see http://codespeak.net/py. This website also contains links to mailing list and IRC channel. - -.. _`initpkg`: http://codespeak.net/svn/py/dist/py/initpkg.py -.. _`py.test documentation`: http://codespeak.net/svn/py/dist/py/documentation/test.txt - Modified: py/trunk/py/doc/greenlet.txt ============================================================================== --- py/trunk/py/doc/greenlet.txt (original) +++ py/trunk/py/doc/greenlet.txt Tue Feb 13 20:01:02 2007 @@ -22,13 +22,12 @@ For example, we can recreate generators; the difference with Python's own generators is that our generators can call nested functions and the nested functions can yield values too. (Additionally, you don't need a "yield" -keyword. See the example in `test_generator.py`_). +keyword. See the example in :source:`py/c-extension/greenlet/test_generator.py`). Greenlets are provided as a C extension module for the regular unmodified interpreter. .. _`Stackless`: http://www.stackless.com -.. _`test_generator.py`: http://codespeak.net/svn/user/arigo/greenlet/test_generator.py Example ------- Deleted: /py/trunk/py/doc/test-distributed.txt ============================================================================== --- /py/trunk/py/doc/test-distributed.txt Tue Feb 13 20:01:02 2007 +++ (empty file) @@ -1,71 +0,0 @@ -======================================================= -The ``py.test`` distributed features - developer's view -======================================================= - -.. contents:: -.. sectnum:: - -starting point: new session objects -=================================== - -There are two new session objects, both located in `rsession.py`_. `RSession` -and `LSession`. `RSession` is responsible for running tests distributedly, -while `LSession` runs tests locally, but with different implementation -details (as well as using details). - -Abstraction layers: -=================== - -Main difference between normal session and `(L|R)Session` is split into -reporter and actual session. Reporter is an object or function (also defined -in `rsession.py`_) which gets all the events (listed in `report.py`_) and -represents them to the user. Default reporter is command line one (to be -precise, there are different reporters for L and R Sessions because of -different needs), but there is existing reporter which runs web server -(`web.py`_) and communicates over XMLHttp requests with the browser. - -Writing down new reporter is relatively easy, way easier than writing session -from a scratch, so one can write down GTK reporter and whatnot. - -Only thing to do is to write down new reporter classs which subclasses -`AbstractReporter` in `reporter.py`_ and overrides all the report_Xxx methods -(each of these method is called when one of the message, defined in -`report.py`_ arrives to reporter). Special consideration is needed when dealing -with ReceivedItemOutcome, which is in details described in `outcome.py`_. - -Another abstraction layer (present only in `LSession`) is runner. Actual -object which runs tests. There are two existing runners: box_runner and -plain_runner, both defined in `local.py`_. box_runner can run tests -after fork, so signals are properly handled (you get error instead of -crash of all the program), plain_runner is running in local process, needed -for --pdb command line option for example. There are several different runners -in my mind, like exec_runner (for implementing --exec), apigen_runner, for -creating documentation, benchamrk_runner for benchmarks etc. etc. - -.. _`rsession.py`: http://codespeak.net/svn/py/dist/py/test/rsession/rsession.py -.. _`report.py`: http://codespeak.net/svn/py/dist/py/test/rsession/report.py -.. _`web.py`: http://codespeak.net/svn/py/dist/py/test/rsession/web.py -.. _`local.py`: http://codespeak.net/svn/py/dist/py/test/rsession/local.py -.. _`reporter.py`: http://codespeak.net/svn/py/dist/py/test/rsession/reporter.py -.. _`outcome.py`: http://codespeak.net/svn/py/dist/py/test/rsession/outcome.py - -Implemented features: -===================== - -Actually most of normal py.test features are implemented in distributed -version. Quite missing is testing LSession under -OSX/Windows or other machines than mine. - -Unique features: -================ - -Besides distribution (which is quite unique for testing framework I guess) -there is boxing code (`box.py`_) which makes possible catching SIGSEGV and -other strange stuff as well as separates memory usage between tests (ie -we're quite sure that memory is not kept allocated during test runs) as long -as it's not gathered during setup/teardown stuff. - -Another unique feature is web server which allows you to track down tests and -how they goes. - -.. _`box.py`: http://codespeak.net/svn/py/dist/py/test/rsession/box.py From hpk at codespeak.net Tue Feb 13 20:10:10 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 13 Feb 2007 20:10:10 +0100 (CET) Subject: [py-svn] r38744 - py/trunk/py/doc Message-ID: <20070213191010.768041008D@code0.codespeak.net> Author: hpk Date: Tue Feb 13 20:10:09 2007 New Revision: 38744 Modified: py/trunk/py/doc/impl-test.txt py/trunk/py/doc/test.txt Log: * adding a section about conftest.py files (removing an XXX) * removing dead links Modified: py/trunk/py/doc/impl-test.txt ============================================================================== --- py/trunk/py/doc/impl-test.txt (original) +++ py/trunk/py/doc/impl-test.txt Tue Feb 13 20:10:09 2007 @@ -134,7 +134,18 @@ writing conftest.py files ----------------------------------- -XXX +You may put conftest.py files containing project-specific +configuration in your project's root directory, it's usually +best to put it just into the same directory level as your +topmost ``__init__.py``. In fact, ``py.test`` performs +an "upwards" search starting from the directory that you specify +to be tested and will lookup configuration values right-to-left. +You may have options that reside e.g. in your home directory +but note that project specific settings will be considered +first. There is a flag that helps you debugging your +conftest.py configurations:: + + py.test --traceconfig adding custom options +++++++++++++++++++++++ Modified: py/trunk/py/doc/test.txt ============================================================================== --- py/trunk/py/doc/test.txt (original) +++ py/trunk/py/doc/test.txt Tue Feb 13 20:10:09 2007 @@ -571,11 +571,7 @@ Changing the behavior of the web based reporter requires `pypy`_ since the javascript is actually generated fom rpython source. -There exists as well `L/Rsession document`_ which discusses in more details -unique features and developement notes. - .. _`pypy`: http://codespeak.net/pypy -.. _`L/Rsession document`: test-distributed.html Future/Planned Features of py.test ================================== From hpk at codespeak.net Tue Feb 13 20:13:42 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 13 Feb 2007 20:13:42 +0100 (CET) Subject: [py-svn] r38745 - py/trunk/py/doc Message-ID: <20070213191342.A2BDF1008D@code0.codespeak.net> Author: hpk Date: Tue Feb 13 20:13:41 2007 New Revision: 38745 Modified: py/trunk/py/doc/path.txt Log: striking another XXX, mentioning limited windows path support. Modified: py/trunk/py/doc/path.txt ============================================================================== --- py/trunk/py/doc/path.txt (original) +++ py/trunk/py/doc/path.txt Tue Feb 13 20:13:41 2007 @@ -187,21 +187,18 @@ >>> len(wc.status().prop_modified) 0 -XXX more examples (look at API) -+++++++++++++++++++++++++++++++++++++++ - -XXX Known problems / limitations =================================== -There are some known issues, most importantly -that using the Subversion Paths requires the -command line `svn` binary and parsing its output -is a bit fragile across versions and locales -(it basically only works with an english locale!). - -XXX note more here +* The SVN path objects require the "svn" command line, + there is currently no support for python bindings. + Parsing the svn output can lead to problems, particularly + regarding if you have a non-english "locales" setting. + +* While the path objects basically work on windows, + there is no attention yet on making unicode paths + work or deal with the famous "8.3" filename issues. Future plans ============ From hpk at codespeak.net Tue Feb 13 20:21:54 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 13 Feb 2007 20:21:54 +0100 (CET) Subject: [py-svn] r38747 - in py/trunk/py: . doc test test/rsession test/rsession/testing test/terminal test/testing Message-ID: <20070213192154.BBB671008D@code0.codespeak.net> Author: hpk Date: Tue Feb 13 20:21:53 2007 New Revision: 38747 Modified: py/trunk/py/__init__.py py/trunk/py/doc/conftest.py py/trunk/py/doc/impl-test.txt py/trunk/py/test/collect.py py/trunk/py/test/defaultconftest.py py/trunk/py/test/doctest.py py/trunk/py/test/representation.py py/trunk/py/test/rsession/executor.py py/trunk/py/test/rsession/repevent.py py/trunk/py/test/rsession/testing/test_executor.py py/trunk/py/test/rsession/testing/test_master.py py/trunk/py/test/session.py py/trunk/py/test/terminal/terminal.py py/trunk/py/test/testing/test_collect.py py/trunk/py/test/testing/test_doctest.py py/trunk/py/test/testing/test_session.py Log: hum, a last-minute namespace change: i missed that fijal didn't move py.test.Item|Function to the py.test.collect namespace at the time. a pypy fix will follow shortly after i merged the dist. Modified: py/trunk/py/__init__.py ============================================================================== --- py/trunk/py/__init__.py (original) +++ py/trunk/py/__init__.py Tue Feb 13 20:21:53 2007 @@ -43,8 +43,8 @@ 'test.collect.Class' : ('./test/collect.py', 'Class'), 'test.collect.Instance' : ('./test/collect.py', 'Instance'), 'test.collect.Generator' : ('./test/collect.py', 'Generator'), - 'test.Item' : ('./test/item.py', 'Item'), - 'test.Function' : ('./test/item.py', 'Function'), + 'test.collect.Item' : ('./test/item.py', 'Item'), + 'test.collect.Function' : ('./test/item.py', 'Function'), # thread related API (still in early design phase) '_thread.WorkerPool' : ('./thread/pool.py', 'WorkerPool'), Modified: py/trunk/py/doc/conftest.py ============================================================================== --- py/trunk/py/doc/conftest.py (original) +++ py/trunk/py/doc/conftest.py Tue Feb 13 20:21:53 2007 @@ -87,13 +87,13 @@ py.test.skip("html file is up to date, use --forcegen to regenerate") #return [] # no need to rebuild -class ReSTSyntaxTest(py.test.Item): +class ReSTSyntaxTest(py.test.collect.Item): def run(self): mypath = self.fspath _checkskip(mypath) restcheck(py.path.svnwc(mypath)) -class DoctestText(py.test.Item): +class DoctestText(py.test.collect.Item): def run(self): # XXX quite nasty... but it works (fixes win32 issues) s = self._normalize_linesep() @@ -141,7 +141,7 @@ if tryfn == name: return CheckLink(name, parent=self, args=(tryfn, path, lineno), obj=call) -class CheckLink(py.test.Function): +class CheckLink(py.test.collect.Function): def setup(self): pass def teardown(self): Modified: py/trunk/py/doc/impl-test.txt ============================================================================== --- py/trunk/py/doc/impl-test.txt (original) +++ py/trunk/py/doc/impl-test.txt Tue Feb 13 20:21:53 2007 @@ -254,7 +254,7 @@ The other extension possibility goes deeper into the machinery and allows you to specify a custom test ``Item`` class which is responsible for setting up and executing an underlying -test. [XXX not working: You can integrate your custom ``py.test.Item`` subclass +test. [XXX not working: You can integrate your custom ``py.test.collect.Item`` subclass by binding an ``Item`` name to a test class.] Or you can extend the collection process for a whole directory tree by putting Items in a ``conftest.py`` configuration file. Modified: py/trunk/py/test/collect.py ============================================================================== --- py/trunk/py/test/collect.py (original) +++ py/trunk/py/test/collect.py Tue Feb 13 20:21:53 2007 @@ -198,7 +198,7 @@ cases. """ if yieldtype is None: - yieldtype = py.test.Item + yieldtype = py.test.collect.Item if isinstance(self, yieldtype): try: self._skipbykeyword(keyword) @@ -208,7 +208,7 @@ excinfo = py.code.ExceptionInfo() reporterror((excinfo, self)) else: - if not isinstance(self, py.test.Item): + if not isinstance(self, py.test.collect.Item): try: if reporterror is not None: reporterror((None, self)) @@ -431,7 +431,7 @@ except IOError: pass # fall back... - for x in self._tryiter((py.test.collect.Generator, py.test.Item)): + for x in self._tryiter((py.test.collect.Generator, py.test.collect.Item)): return x._getsortvalue() class Instance(PyCollectorMixin, Collector): Modified: py/trunk/py/test/defaultconftest.py ============================================================================== --- py/trunk/py/test/defaultconftest.py (original) +++ py/trunk/py/test/defaultconftest.py Tue Feb 13 20:21:53 2007 @@ -5,7 +5,7 @@ Directory = py.test.collect.Directory Class = py.test.collect.Class Generator = py.test.collect.Generator -Function = py.test.Function +Function = py.test.collect.Function Instance = py.test.collect.Instance conf_iocapture = "fd" # overridable from conftest.py Modified: py/trunk/py/test/doctest.py ============================================================================== --- py/trunk/py/test/doctest.py (original) +++ py/trunk/py/test/doctest.py Tue Feb 13 20:21:53 2007 @@ -1,6 +1,6 @@ import py -class DoctestText(py.test.Item): +class DoctestText(py.test.collect.Item): def _setcontent(self, content): self._content = content Modified: py/trunk/py/test/representation.py ============================================================================== --- py/trunk/py/test/representation.py (original) +++ py/trunk/py/test/representation.py Tue Feb 13 20:21:53 2007 @@ -40,7 +40,7 @@ self.out.line(prefix + source[i]) def repr_item_info(self, item): - """ This method represents py.test.Item info (path and module) + """ This method represents py.test.collect.Item info (path and module) """ root = item.fspath modpath = item._getmodpath() Modified: py/trunk/py/test/rsession/executor.py ============================================================================== --- py/trunk/py/test/rsession/executor.py (original) +++ py/trunk/py/test/rsession/executor.py Tue Feb 13 20:21:53 2007 @@ -44,7 +44,7 @@ excinfo = e.excinfo else: excinfo = py.code.ExceptionInfo() - if isinstance(self.item, py.test.Function): + if isinstance(self.item, py.test.collect.Function): fun = self.item.obj # hope this is stable code = py.code.Code(fun) excinfo.traceback = excinfo.traceback.cut( @@ -82,7 +82,7 @@ """ We want to trace *only* function objects here. Unsure what to do with custom collectors at all """ - if hasattr(self.item, 'obj') and type(self.item) is py.test.Function: + if hasattr(self.item, 'obj') and type(self.item) is py.test.collect.Function: self.item.execute = self.wrap_underlaying self.item.run() Modified: py/trunk/py/test/rsession/repevent.py ============================================================================== --- py/trunk/py/test/rsession/repevent.py (original) +++ py/trunk/py/test/rsession/repevent.py Tue Feb 13 20:21:53 2007 @@ -11,7 +11,7 @@ # pass ##def report_error(excinfo): -## if isinstance(excinfo, py.test.Item.Skipped): +## if isinstance(excinfo, py.test.collect.Item.Skipped): ## # we need to dispatch this info ## report(Skipped(excinfo)) ## else: Modified: py/trunk/py/test/rsession/testing/test_executor.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_executor.py (original) +++ py/trunk/py/test/rsession/testing/test_executor.py Tue Feb 13 20:21:53 2007 @@ -12,7 +12,7 @@ if py.std.sys.platform == "win32": py.test.skip("skipping executor tests (some require os.fork)") -class Item(py.test.Item): +class Item(py.test.collect.Item): def __init__(self, name, config): super(Item, self).__init__(name) self._config = config Modified: py/trunk/py/test/rsession/testing/test_master.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_master.py (original) +++ py/trunk/py/test/rsession/testing/test_master.py Tue Feb 13 20:21:53 2007 @@ -49,7 +49,7 @@ def _getremoteerror(self): return "blah" -class Item(py.test.Item): +class Item(py.test.collect.Item): def _get_collector_trail(self): return (self.name,) Modified: py/trunk/py/test/session.py ============================================================================== --- py/trunk/py/test/session.py (original) +++ py/trunk/py/test/session.py Tue Feb 13 20:21:53 2007 @@ -20,7 +20,7 @@ def footer(self, colitems): """ teardown any resources after a test run. """ - py.test.Function._state.teardown_all() + py.test.collect.Function._state.teardown_all() if not self.config.option.nomagic: py.magic.revoke(assertion=1) @@ -99,9 +99,9 @@ colitem.finishcapture() def run(self, colitem): - if self.config.option.collectonly and isinstance(colitem, py.test.Item): + if self.config.option.collectonly and isinstance(colitem, py.test.collect.Item): return - if isinstance(colitem, py.test.Item): + if isinstance(colitem, py.test.collect.Item): colitem._skipbykeyword(self.config.option.keyword) res = colitem.run() if res is None: Modified: py/trunk/py/test/terminal/terminal.py ============================================================================== --- py/trunk/py/test/terminal/terminal.py (original) +++ py/trunk/py/test/terminal/terminal.py Tue Feb 13 20:21:53 2007 @@ -75,7 +75,7 @@ def start_Item(self, colitem): if self.config.option.verbose >= 1: - if isinstance(colitem, py.test.Item): + if isinstance(colitem, py.test.collect.Item): realpath, lineno = colitem._getpathlineno() location = "%s:%d" % (realpath.basename, lineno+1) self.out.write("%-20s %s " % (location, colitem._getmodpath())) @@ -100,7 +100,7 @@ resultstring = self.repr_progress_module_result(colitem, outcome) if resultstring: self.out.line(" - " + resultstring) - if isinstance(colitem, py.test.Item): + if isinstance(colitem, py.test.collect.Item): if self.config.option.verbose >= 1: resultstring = self.repr_progress_long_result(colitem, outcome) resultstring += " (%.2f)" % (colitem.elapsedtime,) Modified: py/trunk/py/test/testing/test_collect.py ============================================================================== --- py/trunk/py/test/testing/test_collect.py (original) +++ py/trunk/py/test/testing/test_collect.py Tue Feb 13 20:21:53 2007 @@ -109,7 +109,7 @@ #class TestWithCustomItem: -# class Item(py.test.Item): +# class Item(py.test.collect.Item): # flag = [] # def execute(self, target, *args): # self.flag.append(42) @@ -146,8 +146,8 @@ l2 = generator.run() assert len(l2) == 2 l2 = generator.multijoin(l2) - assert isinstance(l2[0], py.test.Function) - assert isinstance(l2[1], py.test.Function) + assert isinstance(l2[0], py.test.collect.Function) + assert isinstance(l2[1], py.test.collect.Function) assert l2[0].name == '[0]' assert l2[1].name == '[1]' @@ -162,8 +162,8 @@ l2 = generator.run() assert len(l2) == 2 l2 = generator.multijoin(l2) - assert isinstance(l2[0], py.test.Function) - assert isinstance(l2[1], py.test.Function) + assert isinstance(l2[0], py.test.collect.Function) + assert isinstance(l2[1], py.test.collect.Function) assert l2[0].name == '[0]' assert l2[1].name == '[1]' @@ -171,7 +171,7 @@ o = tmpdir.ensure('customconfigtest', dir=1) o.ensure('conftest.py').write("""if 1: import py - class MyFunction(py.test.Function): + class MyFunction(py.test.collect.Function): pass class Directory(py.test.collect.Directory): def filefilter(self, fspath): @@ -200,7 +200,7 @@ config = py.test.config._reparse([x]) #print "checking that %s returns custom items" % (x,) col = config._getcollector(x) - assert len(list(col._tryiter(py.test.Item))) == 2 + assert len(list(col._tryiter(py.test.collect.Item))) == 2 #assert items[1].__class__.__name__ == 'MyFunction' # test that running a session works from the directories @@ -227,7 +227,7 @@ o = tmpdir.ensure('customconfigtest_nonpython', dir=1) o.ensure('conftest.py').write("""if 1: import py - class CustomItem(py.test.Item): + class CustomItem(py.test.collect.Item): def run(self): pass @@ -247,7 +247,7 @@ print "checking that %s returns custom items" % (x,) config = py.test.config._reparse([x]) col = config._getcollector(x) - assert len(list(col._tryiter(py.test.Item))) == 1 + assert len(list(col._tryiter(py.test.collect.Item))) == 1 #assert items[1].__class__.__name__ == 'MyFunction' # test that running a session works from the directories @@ -337,7 +337,7 @@ try: conf.option.forcegen = 1 col = py.test.collect.Directory(rootdir) - x = list(col._tryiter(yieldtype=py.test.Function)) + x = list(col._tryiter(yieldtype=py.test.collect.Function)) finally: conf.option.forcegen = old Modified: py/trunk/py/test/testing/test_doctest.py ============================================================================== --- py/trunk/py/test/testing/test_doctest.py (original) +++ py/trunk/py/test/testing/test_doctest.py Tue Feb 13 20:21:53 2007 @@ -37,7 +37,7 @@ #print "checking that %s returns custom items" % (x,) config = py.test.config._reparse([x]) col = config._getcollector(x) - items = list(col._tryiter(py.test.Item)) + items = list(col._tryiter(py.test.collect.Item)) assert len(items) == 1 assert isinstance(items[0], DoctestText) Modified: py/trunk/py/test/testing/test_session.py ============================================================================== --- py/trunk/py/test/testing/test_session.py (original) +++ py/trunk/py/test/testing/test_session.py Tue Feb 13 20:21:53 2007 @@ -192,7 +192,7 @@ """)) conftest = o.join('conftest.py').write(py.code.Source(""" import py - class Function(py.test.Function): + class Function(py.test.collect.Function): def startcapture(self): self._mycapture = None From fijal at codespeak.net Tue Feb 13 20:30:20 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Tue, 13 Feb 2007 20:30:20 +0100 (CET) Subject: [py-svn] r38749 - py/trunk/py/test/rsession/webdata Message-ID: <20070213193020.B07661008D@code0.codespeak.net> Author: fijal Date: Tue Feb 13 20:30:19 2007 New Revision: 38749 Modified: py/trunk/py/test/rsession/webdata/source.js Log: Regenerated js Modified: py/trunk/py/test/rsession/webdata/source.js ============================================================================== Binary files. No diff available. From hpk at codespeak.net Tue Feb 13 20:30:34 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 13 Feb 2007 20:30:34 +0100 (CET) Subject: [py-svn] r38750 - py/trunk/py/doc Message-ID: <20070213193034.CB72E10061@code0.codespeak.net> Author: hpk Date: Tue Feb 13 20:30:34 2007 New Revision: 38750 Modified: py/trunk/py/doc/future.txt Log: mention command line completion Modified: py/trunk/py/doc/future.txt ============================================================================== --- py/trunk/py/doc/future.txt (original) +++ py/trunk/py/doc/future.txt Tue Feb 13 20:30:34 2007 @@ -119,6 +119,15 @@ .. _`reiserfs v4 features`: http://www.namesys.com/v4/v4.html +Integrate interactive completion +================================== + +It'd be nice to integrate the bash-like +rlcompleter2_ python command line completer +into the py lib, and making it work remotely +and with pdb. + +.. _rlcompleter2: http://codespeak.net/rlcompleter2/ Consider more features ================================== From hpk at codespeak.net Tue Feb 13 20:56:07 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 13 Feb 2007 20:56:07 +0100 (CET) Subject: [py-svn] r38752 - py/dist Message-ID: <20070213195607.A7BFD100A6@code0.codespeak.net> Author: hpk Date: Tue Feb 13 20:56:06 2007 New Revision: 38752 Added: py/dist/ - copied from r38751, py/trunk/ Log: copying trunk to dist From hpk at codespeak.net Tue Feb 13 21:09:26 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 13 Feb 2007 21:09:26 +0100 (CET) Subject: [py-svn] r38755 - in py/trunk/py/test/rsession: . testing Message-ID: <20070213200926.864AB100A7@code0.codespeak.net> Author: hpk Date: Tue Feb 13 21:09:25 2007 New Revision: 38755 Modified: py/trunk/py/test/rsession/reporter.py py/trunk/py/test/rsession/testing/test_hostmanage.py Log: fix reporting of duplicate remote host rsyncs and adding an XXX test (not easy to test ssh hosts because it modifies remote state) Modified: py/trunk/py/test/rsession/reporter.py ============================================================================== --- py/trunk/py/test/rsession/reporter.py (original) +++ py/trunk/py/test/rsession/reporter.py Tue Feb 13 21:09:25 2007 @@ -65,7 +65,7 @@ hostrepr, item.remotepath) else: print "%15s: skip duplicate rsync to %r" % ( - hostrepr, item.root) + hostrepr, item.remotepath) else: print "%15s: rsync %r to remote %r" % (hostrepr, item.root.basename, Modified: py/trunk/py/test/rsession/testing/test_hostmanage.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_hostmanage.py (original) +++ py/trunk/py/test/rsession/testing/test_hostmanage.py Tue Feb 13 21:09:25 2007 @@ -260,6 +260,19 @@ assert not host.relpath assert events + def XXXtest_ssh_rsync_samehost_twice(self): + XXX we have no easy way to have a temp directory remotely! + option = py.test.config.option + if option.sshtarget is None: + py.test.skip("no known ssh target, use -S to set one") + host1 = HostInfo("%s" % (option.sshtarget, )) + host2 = HostInfo("%s" % (option.sshtarget, )) + hm = HostManager(config, hosts=[host1, host2]) + events = [] + hm.init_rsync(events.append) + print events + assert 0 + def test_getpath_relto_home(): x = getpath_relto_home("hello") assert x == py.path.local._gethomedir().join("hello") From hpk at codespeak.net Tue Feb 13 21:40:30 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 13 Feb 2007 21:40:30 +0100 (CET) Subject: [py-svn] r38760 - py/trunk/py/test/rsession/testing Message-ID: <20070213204030.EF479100AB@code0.codespeak.net> Author: hpk Date: Tue Feb 13 21:40:29 2007 New Revision: 38760 Modified: py/trunk/py/test/rsession/testing/test_hostmanage.py Log: fix ehem syntax ehem error Modified: py/trunk/py/test/rsession/testing/test_hostmanage.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_hostmanage.py (original) +++ py/trunk/py/test/rsession/testing/test_hostmanage.py Tue Feb 13 21:40:29 2007 @@ -261,7 +261,7 @@ assert events def XXXtest_ssh_rsync_samehost_twice(self): - XXX we have no easy way to have a temp directory remotely! + #XXX we have no easy way to have a temp directory remotely! option = py.test.config.option if option.sshtarget is None: py.test.skip("no known ssh target, use -S to set one") From hpk at codespeak.net Tue Feb 13 22:43:33 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 13 Feb 2007 22:43:33 +0100 (CET) Subject: [py-svn] r38761 - py/trunk/py/doc Message-ID: <20070213214333.D3DA4100A6@code0.codespeak.net> Author: hpk Date: Tue Feb 13 22:43:32 2007 New Revision: 38761 Modified: py/trunk/py/doc/confrest.py Log: split Page methods for menubar generation allowing to override A link generation for docs and for apigen pages. Modified: py/trunk/py/doc/confrest.py ============================================================================== --- py/trunk/py/doc/confrest.py (original) +++ py/trunk/py/doc/confrest.py Tue Feb 13 22:43:32 2007 @@ -22,8 +22,28 @@ self._root = html.html(self.head, self.body) self.fill() - def fill(self): + def a_docref(self, name, relhtmlpath): + return html.a(name, class_="menu", href=relhtmlpath) + + def a_apigenref(self, name, relhtml): apigen_relpath = get_apigen_relpath() + path = apigen_relpath.rstrip("/") + "/" + relhtml + return html.a(name, href=path, class_="menu") + + def fill_menubar(self): + self.menubar = html.div( + self.a_docref("index", "index.html"), " ", + self.a_apigenref("api", "api/index.html"), " ", + self.a_apigenref("source", "source/index.html"), " ", + self.a_docref("contact", "contact.html"), " ", + self.a_docref("download", "download.html"), " ", + html.a("contact", href="contact.html", class_="menu"), " ", + html.a("download", href="download.html", class_="menu"), " ", + id="menubar", + ) + + + def fill(self): content_type = "%s;charset=%s" %(self.type, self.encoding) self.head.append(html.title(self.title)) self.head.append(html.meta(name="Content-Type", content=content_type)) @@ -32,16 +52,8 @@ html.link(href=self.stylesheeturl, media="screen", rel="stylesheet", type="text/css")) - self.menubar = html.div( - html.a("index", href="index.html", class_="menu"), " ", - html.a("api", href=apigen_relpath + "api/index.html", class_="menu"), - " ", - html.a("source", href=apigen_relpath + "source/index.html", - class_="menu"), " ", - html.a("contact", href="contact.html", class_="menu"), " ", - html.a("download", href="download.html", class_="menu"), " ", - id="menubar", - ) + self.fill_menubar() + self.metaspace = html.div( html.div(self.title, class_="project_title"), self.menubar, From hpk at codespeak.net Tue Feb 13 22:56:43 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 13 Feb 2007 22:56:43 +0100 (CET) Subject: [py-svn] r38762 - py/trunk/py/doc Message-ID: <20070213215643.3A393100A8@code0.codespeak.net> Author: hpk Date: Tue Feb 13 22:56:42 2007 New Revision: 38762 Modified: py/trunk/py/doc/confrest.py Log: slight sanitizing and allow to change sep in one place Modified: py/trunk/py/doc/confrest.py ============================================================================== --- py/trunk/py/doc/confrest.py (original) +++ py/trunk/py/doc/confrest.py Tue Feb 13 22:56:42 2007 @@ -31,17 +31,21 @@ return html.a(name, href=path, class_="menu") def fill_menubar(self): - self.menubar = html.div( - self.a_docref("index", "index.html"), " ", - self.a_apigenref("api", "api/index.html"), " ", - self.a_apigenref("source", "source/index.html"), " ", - self.a_docref("contact", "contact.html"), " ", - self.a_docref("download", "download.html"), " ", - html.a("contact", href="contact.html", class_="menu"), " ", - html.a("download", href="download.html", class_="menu"), " ", - id="menubar", - ) - + items = [ + self.a_docref("index", "index.html"), + self.a_apigenref("api", "api/index.html"), + self.a_apigenref("source", "source/index.html"), + self.a_docref("contact", "contact.html"), + self.a_docref("download", "download.html"), + html.a("contact", href="contact.html", class_="menu"), + html.a("download", href="download.html", class_="menu"), + ] + items2 = [items.pop(0)] + sep = " " + for item in items: + items2.append(sep) + items2.append(item) + self.menubar = html.div(id="menubar", *items2) def fill(self): content_type = "%s;charset=%s" %(self.type, self.encoding) From hpk at codespeak.net Tue Feb 13 23:20:06 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Tue, 13 Feb 2007 23:20:06 +0100 (CET) Subject: [py-svn] r38763 - py/trunk/py/apigen Message-ID: <20070213222006.562F4100A8@code0.codespeak.net> Author: hpk Date: Tue Feb 13 23:20:05 2007 New Revision: 38763 Modified: py/trunk/py/apigen/layout.py Log: overriding the new a_docref and a_apigenref Page helper methods, getting rid of update_menubar_links Modified: py/trunk/py/apigen/layout.py ============================================================================== --- py/trunk/py/apigen/layout.py (original) +++ py/trunk/py/apigen/layout.py Tue Feb 13 23:20:05 2007 @@ -27,19 +27,24 @@ def fill(self): super(LayoutPage, self).fill() - self.update_menubar_links(self.menubar) self.body.insert(0, self.nav) - def update_menubar_links(self, node): - docrelpath = py.std.os.environ.get('APIGEN_DOCRELPATH', '../py/doc') - if not docrelpath.endswith('/'): - docrelpath += '/' - for item in node: - if not isinstance(item, py.xml.Tag): - continue - if (item.__class__.__name__ == 'a' and hasattr(item.attr, 'href') - and not item.attr.href.startswith('http://')): - item.attr.href = self.relpath + docrelpath + item.attr.href + def _getdocrelpath(self, default="../py/doc"): + docrel = py.std.os.environ.get("APIGEN_DOCRELPATH", default) + return docrel.rstrip("/") + "/" + + def a_docref(self, name, relhtmlpath): + docrelpath = self._getdocrelpath() + relnew = self.relpath + docrelpath + relhtmlpath + return super(LayoutPage, self).a_docref(name, relnew) + + def a_apigenref(self, name, relhtmlpath): + # XXX the path construction is probably rather too complicated + # but i reused the same logic that was there + # before. + docrelpath = self._getdocrelpath() + relnew = self.relpath + docrelpath + relhtmlpath + return super(LayoutPage, self).a_apigenref(name, relnew) def setup_scripts_styles(self, copyto=None): for path, name in self.stylesheets: From hpk at codespeak.net Wed Feb 14 00:20:44 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 14 Feb 2007 00:20:44 +0100 (CET) Subject: [py-svn] r38766 - py/trunk/py/doc Message-ID: <20070213232044.E0EE9100A3@code0.codespeak.net> Author: hpk Date: Wed Feb 14 00:20:43 2007 New Revision: 38766 Modified: py/trunk/py/doc/confrest.py Log: naming consistency Modified: py/trunk/py/doc/confrest.py ============================================================================== --- py/trunk/py/doc/confrest.py (original) +++ py/trunk/py/doc/confrest.py Wed Feb 14 00:20:43 2007 @@ -25,9 +25,9 @@ def a_docref(self, name, relhtmlpath): return html.a(name, class_="menu", href=relhtmlpath) - def a_apigenref(self, name, relhtml): + def a_apigenref(self, name, relhtmlpath): apigen_relpath = get_apigen_relpath() - path = apigen_relpath.rstrip("/") + "/" + relhtml + path = apigen_relpath.rstrip("/") + "/" + relhtmlpath return html.a(name, href=path, class_="menu") def fill_menubar(self): From guido at codespeak.net Wed Feb 14 00:56:59 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Wed, 14 Feb 2007 00:56:59 +0100 (CET) Subject: [py-svn] r38770 - in py/trunk/py: . apigen apigen/testing bin doc misc/testing Message-ID: <20070213235659.46606100A9@code0.codespeak.net> Author: guido Date: Wed Feb 14 00:56:57 2007 New Revision: 38770 Modified: py/trunk/py/apigen/apigen.py py/trunk/py/apigen/htmlgen.py py/trunk/py/apigen/layout.py py/trunk/py/apigen/testing/test_apigen_example.py py/trunk/py/apigen/testing/test_apigen_functional.py py/trunk/py/bin/_docgen.py py/trunk/py/bin/_update_website.py py/trunk/py/conftest.py py/trunk/py/doc/confrest.py py/trunk/py/doc/conftest.py py/trunk/py/misc/testing/test_update_website.py Log: Removed option --apigen_relpath, added options --docpath and --apigenpath, both of which are not only used for building the navigation, but also to actually tell py.test where docs should be generated. Also cleaned up and fixed the situation regarding relative links in the navigation menu and stylesheet/js paths. Modified: py/trunk/py/apigen/apigen.py ============================================================================== --- py/trunk/py/apigen/apigen.py (original) +++ py/trunk/py/apigen/apigen.py Wed Feb 14 00:56:57 2007 @@ -1,7 +1,4 @@ """ run 'py.test --apigen=' to get documentation exported - - exports to /tmp/output by default, set the environment variable - 'APIGEN_TARGET' to override """ import os @@ -11,6 +8,7 @@ from py.__.apigen import linker from py.__.apigen import project from py.__.apigen.tracer.docstorage import pkg_to_dict +from py.__.doc.conftest import get_apigenpath from layout import LayoutPage @@ -41,10 +39,8 @@ proj = project.Project() # output dir - if 'APIGEN_TARGET' in os.environ: - targetdir = py.path.local(os.environ['APIGEN_TARGET']) - else: - targetdir = pkgdir.dirpath().join('apigen') + from py.__.conftest import option + targetdir = get_apigenpath() targetdir.ensure(dir=True) # find out what to build Modified: py/trunk/py/apigen/htmlgen.py ============================================================================== --- py/trunk/py/apigen/htmlgen.py (original) +++ py/trunk/py/apigen/htmlgen.py Wed Feb 14 00:56:57 2007 @@ -119,10 +119,9 @@ ret[ns].append(itempath) return ret -def wrap_page(project, title, contentel, navel, relbase, basepath, +def wrap_page(project, title, targetpath, contentel, navel, basepath, pageclass): - page = pageclass(project, title, nav=navel, encoding='UTF-8', - relpath=relbase) + page = pageclass(project, title, targetpath, nav=navel, encoding='UTF-8') page.set_content(contentel) page.setup_scripts_styles(basepath) return page @@ -180,7 +179,7 @@ targetpath = self.base.join(reltargetpath) relbase= relpath('%s%s' % (targetpath.dirpath(), targetpath.sep), self.base.strpath + '/') - page = wrap_page(self.project, title, tag, nav, relbase, self.base, + page = wrap_page(self.project, title, targetpath, tag, nav, self.base, self.pageclass) # we write the page with _temporary_ hrefs here, need to be replaced # from the TempLinker later Modified: py/trunk/py/apigen/layout.py ============================================================================== --- py/trunk/py/apigen/layout.py (original) +++ py/trunk/py/apigen/layout.py Wed Feb 14 00:56:57 2007 @@ -6,6 +6,7 @@ import py from py.__.doc import confrest from py.__.apigen import linker +from py.__.doc.conftest import get_apigenpath, get_docpath here = py.magic.autopath().dirpath() @@ -18,10 +19,14 @@ def __init__(self, *args, **kwargs): self.nav = kwargs.pop('nav') - self.relpath = kwargs.pop('relpath') super(LayoutPage, self).__init__(*args, **kwargs) + self.relpath = self.get_relpath() self.project.logo.attr.id = 'logo' + def get_relpath(self): + return linker.relpath(self.targetpath.strpath, + get_apigenpath().strpath) + '/' + def set_content(self, contentel): self.contentspace.append(contentel) @@ -29,23 +34,6 @@ super(LayoutPage, self).fill() self.body.insert(0, self.nav) - def _getdocrelpath(self, default="../py/doc"): - docrel = py.std.os.environ.get("APIGEN_DOCRELPATH", default) - return docrel.rstrip("/") + "/" - - def a_docref(self, name, relhtmlpath): - docrelpath = self._getdocrelpath() - relnew = self.relpath + docrelpath + relhtmlpath - return super(LayoutPage, self).a_docref(name, relnew) - - def a_apigenref(self, name, relhtmlpath): - # XXX the path construction is probably rather too complicated - # but i reused the same logic that was there - # before. - docrelpath = self._getdocrelpath() - relnew = self.relpath + docrelpath + relhtmlpath - return super(LayoutPage, self).a_apigenref(name, relnew) - def setup_scripts_styles(self, copyto=None): for path, name in self.stylesheets: if copyto: Modified: py/trunk/py/apigen/testing/test_apigen_example.py ============================================================================== --- py/trunk/py/apigen/testing/test_apigen_example.py (original) +++ py/trunk/py/apigen/testing/test_apigen_example.py Wed Feb 14 00:56:57 2007 @@ -5,6 +5,7 @@ from py.__.apigen.htmlgen import * from py.__.apigen.tracer.docstorage import DocStorage, DocStorageAccessor from py.__.apigen.tracer.tracer import Tracer +from py.__.apigen.layout import LayoutPage from py.__.apigen.project import Project from py.__.test.web import webcheck from py.__.apigen.conftest import option @@ -94,6 +95,10 @@ #"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">\n""" + unicode(h) #_checkhtml(newstring) +class LayoutTestPage(LayoutPage): + def get_relpath(self): + return '../' + class AbstractBuilderTest(object): def setup_class(cls): temp = py.test.ensuretemp('apigen_example') @@ -121,6 +126,7 @@ self.spb = SourcePageBuilder(base, linker, self.fs_root.join(self.pkg_name), self.project) + self.apb.pageclass = self.spb.pageclass = LayoutTestPage class TestApiPageBuilder(AbstractBuilderTest): def test_build_callable_view(self): @@ -396,7 +402,7 @@ html = funcsource.read() print html run_string_sequence_test(html, [ - 'href="../../style.css"', + 'href="../style.css"', '
pkg', 'someclass.py', 'somesubclass.py', @@ -410,7 +416,7 @@ html = pkgindex.read() print html run_string_sequence_test(html, [ - 'href="../../style.css"', + 'href="../style.css"', 'pkg', 'func.py', 'someclass.py', Modified: py/trunk/py/apigen/testing/test_apigen_functional.py ============================================================================== --- py/trunk/py/apigen/testing/test_apigen_functional.py (original) +++ py/trunk/py/apigen/testing/test_apigen_functional.py Wed Feb 14 00:56:57 2007 @@ -125,10 +125,10 @@ pydir = py.magic.autopath().dirpath().dirpath().dirpath() pakdir = fs_root.join('pak') if py.std.sys.platform == 'win32': - cmd = ('set APIGEN_TARGET=%s && set PYTHONPATH=%s && ' + cmd = ('set APIGENPATH=%s && set PYTHONPATH=%s && ' 'python "%s/bin/py.test"') % (tempdir, fs_root, pydir) else: - cmd = ('APIGEN_TARGET="%s" PYTHONPATH="%s" ' + cmd = ('APIGENPATH="%s" PYTHONPATH="%s" ' 'python "%s/bin/py.test"') % (tempdir, fs_root, pydir) try: output = py.process.cmdexec('%s --apigen="%s/apigen.py" "%s"' % ( Modified: py/trunk/py/bin/_docgen.py ============================================================================== --- py/trunk/py/bin/_docgen.py (original) +++ py/trunk/py/bin/_docgen.py Wed Feb 14 00:56:57 2007 @@ -23,7 +23,7 @@ run_tests(pypath, 'APIGEN_TARGET="%s/apigen" APIGEN_DOCRELPATH="../"' % ( targetpath,), - testargs + ' --apigen="%s/apigen/apigen.py"' % (pypath,)) + '%s --apigen="%s/apigen/apigen.py"' % (testargs, pypath)) def build_docs(targetpath, testargs): docpath = pypath.join('doc') Modified: py/trunk/py/bin/_update_website.py ============================================================================== --- py/trunk/py/bin/_update_website.py (original) +++ py/trunk/py/bin/_update_website.py Wed Feb 14 00:56:57 2007 @@ -22,32 +22,32 @@ rs.add_target(gateway, remotepath, delete=True) rs.send() -def run_tests(pkgpath, args='', captureouterr=False): +def run_tests(pkgpath, apigenpath, args='', captureouterr=False): """ run the unit tests and build the docs """ pypath = py.__package__.getpath() pytestpath = pypath.join('bin/py.test') # XXX this would need a Windows specific version if we want to allow # running this script on that platform, but currently --apigen doesn't # work there anyway... - apigenpath = pkgpath.join('apigen/apigen.py') # XXX be more general here? - if not apigenpath.check(file=True): - apigenpath = pypath.join('apigen/apigen.py') - cmd = 'PYTHONPATH="%s:%s" python "%s" %s --apigen="%s" "%s"' % ( - pypath.dirpath(), - pkgpath.dirpath(), - pytestpath, - args, - apigenpath, - pkgpath, - ) + apigenscript = pkgpath.join('apigen/apigen.py') # XXX be more general here? + if not apigenscript.check(file=True): + apigenscript = pypath.join('apigen/apigen.py') + cmd = ('APIGENPATH="%s" PYTHONPATH="%s:%s" python ' + '"%s" %s --apigen="%s" "%s"' % (apigenpath, pypath.dirpath(), + pkgpath.dirpath(), pytestpath, + args, apigenscript, + pkgpath)) if captureouterr: cmd += ' > /dev/null 2>&1' - status = py.std.os.system(cmd) - return status + try: + output = py.process.cmdexec(cmd) + except py.error.Error, e: + return e.err or str(e) + return None def main(pkgpath, apidocspath, rhost, rpath, args='', ignorefail=False): print 'running tests' - errors = run_tests(pkgpath, args) + errors = run_tests(pkgpath, apidocspath, args) if errors: print >>sys.stderr, \ 'Errors while running the unit tests: %s' % (errors,) Modified: py/trunk/py/conftest.py ============================================================================== --- py/trunk/py/conftest.py (original) +++ py/trunk/py/conftest.py Wed Feb 14 00:56:57 2007 @@ -19,11 +19,22 @@ import py Option = py.test.config.Option +here = py.magic.autopath().dirpath() + option = py.test.config.addoptions("execnet options", Option('-S', '', action="store", dest="sshtarget", default=None, help=("target to run tests requiring ssh, e.g. " "user at codespeak.net")), + Option('', '--apigenpath', + action="store", dest="apigenpath", + default=here.join("../apigen").strpath, + type="string", + help="absolute path to where apigen docs are built"), + Option('', '--docpath', + action='store', dest='docpath', + default=here.join('doc').strpath, type='string', + help='absolute path to where the docs are built'), ) dist_rsync_roots = ['.'] Modified: py/trunk/py/doc/confrest.py ============================================================================== --- py/trunk/py/doc/confrest.py (original) +++ py/trunk/py/doc/confrest.py Wed Feb 14 00:56:57 2007 @@ -1,7 +1,8 @@ import py from py.__.misc.rest import convert_rest_html, strip_html_header from py.__.misc.difftime import worded_time -from py.__.doc.conftest import get_apigen_relpath +from py.__.doc.conftest import get_apigenpath, get_docpath +from py.__.apigen.linker import relpath mydir = py.magic.autopath().dirpath() html = py.xml.html @@ -10,9 +11,11 @@ doctype = ('\n') - def __init__(self, project, title, stylesheeturl=None, type="text/html", encoding="ISO-8859-1"): + def __init__(self, project, title, targetpath, stylesheeturl=None, + type="text/html", encoding="ISO-8859-1"): self.project = project self.title = project.prefix_title + title + self.targetpath = targetpath self.stylesheeturl = stylesheeturl self.type = type self.encoding = encoding @@ -23,22 +26,26 @@ self.fill() def a_docref(self, name, relhtmlpath): - return html.a(name, class_="menu", href=relhtmlpath) + docpath = get_docpath() + return html.a(name, class_="menu", + href=relpath(self.targetpath.strpath, + docpath.join(relhtmlpath).strpath)) def a_apigenref(self, name, relhtmlpath): - apigen_relpath = get_apigen_relpath() - path = apigen_relpath.rstrip("/") + "/" + relhtmlpath - return html.a(name, href=path, class_="menu") + apipath = get_apigenpath() + return html.a(name, class_="menu", + href=relpath(self.targetpath.strpath, + apipath.join(relhtmlpath).strpath)) def fill_menubar(self): items = [ - self.a_docref("index", "index.html"), - self.a_apigenref("api", "api/index.html"), - self.a_apigenref("source", "source/index.html"), - self.a_docref("contact", "contact.html"), - self.a_docref("download", "download.html"), - html.a("contact", href="contact.html", class_="menu"), - html.a("download", href="download.html", class_="menu"), + self.a_docref("index", "index.html"), + self.a_apigenref("api", "api/index.html"), + self.a_apigenref("source", "source/index.html"), + self.a_docref("contact", "contact.html"), + self.a_docref("download", "download.html"), + html.a("contact", href="contact.html", class_="menu"), + html.a("download", href="download.html", class_="menu"), ] items2 = [items.pop(0)] sep = " " @@ -47,26 +54,26 @@ items2.append(item) self.menubar = html.div(id="menubar", *items2) - def fill(self): - content_type = "%s;charset=%s" %(self.type, self.encoding) - self.head.append(html.title(self.title)) + def fill(self): + content_type = "%s;charset=%s" %(self.type, self.encoding) + self.head.append(html.title(self.title)) self.head.append(html.meta(name="Content-Type", content=content_type)) - if self.stylesheeturl: + if self.stylesheeturl: self.head.append( - html.link(href=self.stylesheeturl, - media="screen", rel="stylesheet", + html.link(href=self.stylesheeturl, + media="screen", rel="stylesheet", type="text/css")) self.fill_menubar() self.metaspace = html.div( - html.div(self.title, class_="project_title"), + html.div(self.title, class_="project_title"), self.menubar, id='metaspace') - self.body.append(self.project.logo) - self.body.append(self.metaspace) + self.body.append(self.project.logo) + self.body.append(self.metaspace) self.contentspace = html.div(id="contentspace") - self.body.append(self.contentspace) + self.body.append(self.contentspace) def unicode(self, doctype=True): page = self._root.unicode() @@ -76,12 +83,14 @@ return page class PyPage(Page): - def fill(self): - super(PyPage, self).fill() + def get_menubar(self): + menubar = super(PyPage, self).get_menubar() # base layout - self.menubar.append( - html.a("issue", href="https://codespeak.net/issue/py-dev/", class_="menu"), - ) + menubar.append( + html.a("issue", href="https://codespeak.net/issue/py-dev/", + class_="menu"), + ) + return menubar def getrealname(username): @@ -99,8 +108,9 @@ return username -class Project: - stylesheet = 'style.css' +class Project: + # string for url, path for local file + stylesheet = mydir.join('style.css') title = "py lib" prefix_title = "" # we have a logo already containing "py lib" encoding = 'latin1' @@ -118,15 +128,23 @@ def process(self, txtpath): encoding = self.encoding content = self.get_content(txtpath, encoding) - stylesheet = self.stylesheet - if not stylesheet.startswith('http') and \ - not txtpath.dirpath(stylesheet).check(): - stylesheet = None + docpath = get_docpath() + reloutputpath = txtpath.new(ext='.html').relto(mydir) + outputpath = docpath.join(reloutputpath) + + stylesheet = self.stylesheet + if isinstance(self.stylesheet, py.path.local): + if not docpath.join(stylesheet.basename).check(): + stylesheet.copy(docpath) + stylesheet = relpath(outputpath.strpath, + docpath.join(stylesheet.basename).strpath) - content = convert_rest_html(content, txtpath, stylesheet=stylesheet, encoding=encoding) + content = convert_rest_html(content, txtpath, + stylesheet=stylesheet, encoding=encoding) content = strip_html_header(content, encoding=encoding) - page = self.Page(self, "[%s] " % txtpath.purebasename, stylesheeturl=stylesheet) + page = self.Page(self, "[%s] " % txtpath.purebasename, + outputpath, stylesheeturl=stylesheet) try: svninfo = txtpath.info() @@ -142,6 +160,5 @@ id = 'docinfoline')) page.contentspace.append(py.xml.raw(content)) - htmlpath = txtpath.new(ext='.html') - htmlpath.write(page.unicode().encode(encoding)) + outputpath.ensure().write(page.unicode().encode(encoding)) Modified: py/trunk/py/doc/conftest.py ============================================================================== --- py/trunk/py/doc/conftest.py (original) +++ py/trunk/py/doc/conftest.py Wed Feb 14 00:56:57 2007 @@ -1,6 +1,10 @@ from __future__ import generators import py from py.__.misc import rest +from py.__.apigen.linker import relpath +import os + +mypath = py.magic.autopath().dirpath() Option = py.test.config.Option option = py.test.config.addoptions("documentation check options", @@ -12,16 +16,24 @@ action="store_true", dest="forcegen", default=False, help="force generation of html files even if they appear up-to-date" ), - Option('', '--apigenrelpath', - action="store", dest="apigen_relpath", default="../../apigen", - type="string", - help=("specify the relative path to apigen (used for link " - "generation)") - ) ) +def get_apigenpath(): + from py.__.conftest import option + path = os.environ.get('APIGENPATH') + if path is None: + path = option.apigenpath + return py.path.local(path) + +def get_docpath(): + from py.__.conftest import option + path = os.environ.get('DOCPATH') + if path is None: + path = option.docpath + return py.path.local(path) + def get_apigen_relpath(): - return py.test.config.option.apigen_relpath.rstrip('\/') + "/" + return relpath(get_apigenpath().strpath, get_docpath().strpath) def deindent(s, sep='\n'): leastspaces = -1 @@ -82,7 +94,7 @@ def _checkskip(lpath): if not option.forcegen: if lpath.ext == '.txt': - htmlpath = lpath.new(ext='.html') + htmlpath = get_docpath().join(lpath.new(ext='.html').relto(mypath)) if htmlpath.check(file=1) and htmlpath.mtime() >= lpath.mtime(): py.test.skip("html file is up to date, use --forcegen to regenerate") #return [] # no need to rebuild Modified: py/trunk/py/misc/testing/test_update_website.py ============================================================================== --- py/trunk/py/misc/testing/test_update_website.py (original) +++ py/trunk/py/misc/testing/test_update_website.py Wed Feb 14 00:56:57 2007 @@ -52,7 +52,10 @@ if py.std.sys.platform == "win32": py.test.skip("update_website is not supposed to be run from win32") pkgpath = setup_pkg('update_website_run_tests') - errors = update_website.run_tests(pkgpath, captureouterr=True) + errors = update_website.run_tests(pkgpath, + pkgpath.dirpath().join('apigen'), + captureouterr=True) + print errors assert not errors assert pkgpath.join('../apigen').check(dir=True) assert pkgpath.join('../apigen/api/sub.foo.html').check(file=True) @@ -63,6 +66,8 @@ pkgpath = setup_pkg('update_website_run_tests_failure') assert not pkgpath.join('../apigen').check(dir=True) pkgpath.ensure('../apigen', file=True) - errors = update_website.run_tests(pkgpath, captureouterr=True) + errors = update_website.run_tests(pkgpath, + pkgpath.dirpath().join('apigen'), + captureouterr=True) assert errors # some error message From guido at codespeak.net Wed Feb 14 01:07:47 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Wed, 14 Feb 2007 01:07:47 +0100 (CET) Subject: [py-svn] r38772 - in py/trunk/py: apigen doc Message-ID: <20070214000747.81394100A8@code0.codespeak.net> Author: guido Date: Wed Feb 14 01:07:45 2007 New Revision: 38772 Modified: py/trunk/py/apigen/apigen.py py/trunk/py/doc/confrest.py Log: Make sure target dir exists before writing files to it. Modified: py/trunk/py/apigen/apigen.py ============================================================================== --- py/trunk/py/apigen/apigen.py (original) +++ py/trunk/py/apigen/apigen.py Wed Feb 14 01:07:45 2007 @@ -41,7 +41,8 @@ # output dir from py.__.conftest import option targetdir = get_apigenpath() - targetdir.ensure(dir=True) + if not targetdir.check(dir=True): + targetdir.ensure(dir=True) # find out what to build all_names = dsa._get_names(filter=lambda x, y: True) Modified: py/trunk/py/doc/confrest.py ============================================================================== --- py/trunk/py/doc/confrest.py (original) +++ py/trunk/py/doc/confrest.py Wed Feb 14 01:07:45 2007 @@ -135,6 +135,8 @@ stylesheet = self.stylesheet if isinstance(self.stylesheet, py.path.local): if not docpath.join(stylesheet.basename).check(): + if not docpath.check(dir=True): + docpath.ensure(dir=True) stylesheet.copy(docpath) stylesheet = relpath(outputpath.strpath, docpath.join(stylesheet.basename).strpath) From guido at codespeak.net Wed Feb 14 01:11:27 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Wed, 14 Feb 2007 01:11:27 +0100 (CET) Subject: [py-svn] r38773 - in py/trunk/py: apigen doc Message-ID: <20070214001127.44F6B100A9@code0.codespeak.net> Author: guido Date: Wed Feb 14 01:11:26 2007 New Revision: 38773 Modified: py/trunk/py/apigen/apigen.py py/trunk/py/doc/confrest.py Log: Using ensure() better (thanks, hpk ;). Modified: py/trunk/py/apigen/apigen.py ============================================================================== --- py/trunk/py/apigen/apigen.py (original) +++ py/trunk/py/apigen/apigen.py Wed Feb 14 01:11:26 2007 @@ -41,8 +41,7 @@ # output dir from py.__.conftest import option targetdir = get_apigenpath() - if not targetdir.check(dir=True): - targetdir.ensure(dir=True) + targetdir.ensure(dir=True) # find out what to build all_names = dsa._get_names(filter=lambda x, y: True) Modified: py/trunk/py/doc/confrest.py ============================================================================== --- py/trunk/py/doc/confrest.py (original) +++ py/trunk/py/doc/confrest.py Wed Feb 14 01:11:26 2007 @@ -135,8 +135,7 @@ stylesheet = self.stylesheet if isinstance(self.stylesheet, py.path.local): if not docpath.join(stylesheet.basename).check(): - if not docpath.check(dir=True): - docpath.ensure(dir=True) + docpath.ensure(dir=True) stylesheet.copy(docpath) stylesheet = relpath(outputpath.strpath, docpath.join(stylesheet.basename).strpath) From hpk at codespeak.net Wed Feb 14 02:03:52 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 14 Feb 2007 02:03:52 +0100 (CET) Subject: [py-svn] r38775 - in py/trunk/py: . doc Message-ID: <20070214010352.92176100A6@code0.codespeak.net> Author: hpk Date: Wed Feb 14 02:03:51 2007 New Revision: 38775 Modified: py/trunk/py/conftest.py py/trunk/py/doc/conftest.py Log: use relative paths, and compute them late Modified: py/trunk/py/conftest.py ============================================================================== --- py/trunk/py/conftest.py (original) +++ py/trunk/py/conftest.py Wed Feb 14 02:03:51 2007 @@ -19,8 +19,6 @@ import py Option = py.test.config.Option -here = py.magic.autopath().dirpath() - option = py.test.config.addoptions("execnet options", Option('-S', '', action="store", dest="sshtarget", default=None, @@ -28,13 +26,13 @@ "user at codespeak.net")), Option('', '--apigenpath', action="store", dest="apigenpath", - default=here.join("../apigen").strpath, + default="../apigen", type="string", - help="absolute path to where apigen docs are built"), + help="relative path to apigen doc output location (relative from py/)"), Option('', '--docpath', action='store', dest='docpath', - default=here.join('doc').strpath, type='string', - help='absolute path to where the docs are built'), + default="doc", type='string', + help="relative path to doc output location (relative from py/)"), ) dist_rsync_roots = ['.'] Modified: py/trunk/py/doc/conftest.py ============================================================================== --- py/trunk/py/doc/conftest.py (original) +++ py/trunk/py/doc/conftest.py Wed Feb 14 02:03:51 2007 @@ -4,6 +4,8 @@ from py.__.apigen.linker import relpath import os +pypkgdir = py.path.local(py.__file__).dirpath() + mypath = py.magic.autopath().dirpath() Option = py.test.config.Option @@ -23,14 +25,14 @@ path = os.environ.get('APIGENPATH') if path is None: path = option.apigenpath - return py.path.local(path) + return pypkgdir.join(path, abs=True) def get_docpath(): from py.__.conftest import option path = os.environ.get('DOCPATH') if path is None: path = option.docpath - return py.path.local(path) + return pypkgdir.join(path, abs=True) def get_apigen_relpath(): return relpath(get_apigenpath().strpath, get_docpath().strpath) From guido at codespeak.net Wed Feb 14 02:18:07 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Wed, 14 Feb 2007 02:18:07 +0100 (CET) Subject: [py-svn] r38776 - py/trunk/py/doc Message-ID: <20070214011807.56076100A9@code0.codespeak.net> Author: guido Date: Wed Feb 14 02:18:06 2007 New Revision: 38776 Modified: py/trunk/py/doc/confrest.py Log: Removed duplicate links. Modified: py/trunk/py/doc/confrest.py ============================================================================== --- py/trunk/py/doc/confrest.py (original) +++ py/trunk/py/doc/confrest.py Wed Feb 14 02:18:06 2007 @@ -44,8 +44,6 @@ self.a_apigenref("source", "source/index.html"), self.a_docref("contact", "contact.html"), self.a_docref("download", "download.html"), - html.a("contact", href="contact.html", class_="menu"), - html.a("download", href="download.html", class_="menu"), ] items2 = [items.pop(0)] sep = " " From guido at codespeak.net Wed Feb 14 02:31:50 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Wed, 14 Feb 2007 02:31:50 +0100 (CET) Subject: [py-svn] r38777 - py/trunk/py/doc Message-ID: <20070214013150.01EF7100A6@code0.codespeak.net> Author: guido Date: Wed Feb 14 02:31:50 2007 New Revision: 38777 Modified: py/trunk/py/doc/conftest.py Log: Fixed api and source link role relative paths. Modified: py/trunk/py/doc/conftest.py ============================================================================== --- py/trunk/py/doc/conftest.py (original) +++ py/trunk/py/doc/conftest.py Wed Feb 14 02:31:50 2007 @@ -35,7 +35,8 @@ return pypkgdir.join(path, abs=True) def get_apigen_relpath(): - return relpath(get_apigenpath().strpath, get_docpath().strpath) + return relpath(get_docpath().strpath + '/', + get_apigenpath().strpath + '/') def deindent(s, sep='\n'): leastspaces = -1 From guido at codespeak.net Wed Feb 14 03:03:53 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Wed, 14 Feb 2007 03:03:53 +0100 (CET) Subject: [py-svn] r38779 - in py/trunk/py/apigen: . testing Message-ID: <20070214020353.ECD8710080@code0.codespeak.net> Author: guido Date: Wed Feb 14 03:03:51 2007 New Revision: 38779 Modified: py/trunk/py/apigen/htmlgen.py py/trunk/py/apigen/testing/test_apigen_example.py Log: Made that the package revision is now shown in the title rather than each file's individual revision. Modified: py/trunk/py/apigen/htmlgen.py ============================================================================== --- py/trunk/py/apigen/htmlgen.py (original) +++ py/trunk/py/apigen/htmlgen.py Wed Feb 14 03:03:51 2007 @@ -170,6 +170,16 @@ return default return relpath +_rev = -1 +def get_package_revision(packageroot): + global _rev + if _rev == -1: + _rev = None + wc = py.path.svnwc(packageroot) + if wc.check(versioned=True): + _rev = py.path.svnwc(packageroot).info().rev + return _rev + # the PageBuilder classes take care of producing the docs (using the stuff # above) class AbstractPageBuilder(object): @@ -319,6 +329,7 @@ _revcache = {} def get_revision(self, path): + return get_package_revision(self.projroot) strpath = path.strpath if strpath in self._revcache: return self._revcache[strpath] @@ -729,6 +740,7 @@ return rev def get_revision(self, dotted_name): + return get_package_revision(self.projroot) if dotted_name in self._revcache: return self._revcache[dotted_name] obj = get_obj(self.dsa, self.pkg, dotted_name) Modified: py/trunk/py/apigen/testing/test_apigen_example.py ============================================================================== --- py/trunk/py/apigen/testing/test_apigen_example.py (original) +++ py/trunk/py/apigen/testing/test_apigen_example.py Wed Feb 14 03:03:51 2007 @@ -326,6 +326,7 @@ _checkhtml(html) def test_get_revision(self): + py.test.skip('XXX changed implementation (temporarily?)') if py.std.sys.platform.startswith('win'): py.test.skip('broken on win32 for some reason (svn caching?), ' 'skipping') @@ -447,6 +448,7 @@ ]) def test_get_revision(self): + py.test.skip('XXX changed implementation (temporarily?)') if py.std.sys.platform.startswith('win'): py.test.skip('broken on win32 for some reason (svn caching?), ' 'skipping') From hpk at codespeak.net Wed Feb 14 03:08:01 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 14 Feb 2007 03:08:01 +0100 (CET) Subject: [py-svn] r38780 - py/dist Message-ID: <20070214020801.8E18810080@code0.codespeak.net> Author: hpk Date: Wed Feb 14 03:08:00 2007 New Revision: 38780 Added: py/dist/ - copied from r38779, py/trunk/ Log: remerging trunk to dist (mostly doc changes but might affect pypy) From hpk at codespeak.net Wed Feb 14 03:29:13 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 14 Feb 2007 03:29:13 +0100 (CET) Subject: [py-svn] r38781 - py/trunk/py/doc Message-ID: <20070214022913.79868100A6@code0.codespeak.net> Author: hpk Date: Wed Feb 14 03:29:11 2007 New Revision: 38781 Modified: py/trunk/py/doc/confrest.py Log: the target docpath needs to be determined per project Modified: py/trunk/py/doc/confrest.py ============================================================================== --- py/trunk/py/doc/confrest.py (original) +++ py/trunk/py/doc/confrest.py Wed Feb 14 03:29:11 2007 @@ -4,7 +4,6 @@ from py.__.doc.conftest import get_apigenpath, get_docpath from py.__.apigen.linker import relpath -mydir = py.magic.autopath().dirpath() html = py.xml.html class Page(object): @@ -107,6 +106,7 @@ class Project: + mydir = py.magic.autopath().dirpath() # string for url, path for local file stylesheet = mydir.join('style.css') title = "py lib" @@ -123,11 +123,14 @@ def get_content(self, txtpath, encoding): return unicode(txtpath.read(), encoding) + def get_docpath(self): + return get_docpath() + def process(self, txtpath): encoding = self.encoding content = self.get_content(txtpath, encoding) - docpath = get_docpath() - reloutputpath = txtpath.new(ext='.html').relto(mydir) + docpath = self.get_docpath() + reloutputpath = txtpath.new(ext='.html').relto(self.mydir) outputpath = docpath.join(reloutputpath) stylesheet = self.stylesheet From hpk at codespeak.net Wed Feb 14 03:30:20 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 14 Feb 2007 03:30:20 +0100 (CET) Subject: [py-svn] r38782 - py/trunk/py/doc Message-ID: <20070214023020.E4699100A6@code0.codespeak.net> Author: hpk Date: Wed Feb 14 03:30:19 2007 New Revision: 38782 Modified: py/trunk/py/doc/confrest.py Log: here as well Modified: py/trunk/py/doc/confrest.py ============================================================================== --- py/trunk/py/doc/confrest.py (original) +++ py/trunk/py/doc/confrest.py Wed Feb 14 03:30:19 2007 @@ -25,7 +25,7 @@ self.fill() def a_docref(self, name, relhtmlpath): - docpath = get_docpath() + docpath = self.project.get_docpath() return html.a(name, class_="menu", href=relpath(self.targetpath.strpath, docpath.join(relhtmlpath).strpath)) From hpk at codespeak.net Wed Feb 14 03:31:05 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 14 Feb 2007 03:31:05 +0100 (CET) Subject: [py-svn] r38783 - py/dist Message-ID: <20070214023105.09FFD100A7@code0.codespeak.net> Author: hpk Date: Wed Feb 14 03:31:05 2007 New Revision: 38783 Added: py/dist/ - copied from r38782, py/trunk/ Log: another dist merge From hpk at codespeak.net Wed Feb 14 12:02:17 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 14 Feb 2007 12:02:17 +0100 (CET) Subject: [py-svn] r38797 - py/trunk/py/apigen Message-ID: <20070214110217.D5915100A9@code0.codespeak.net> Author: hpk Date: Wed Feb 14 12:02:16 2007 New Revision: 38797 Modified: py/trunk/py/apigen/htmlgen.py Log: fixing the too global revision caching Modified: py/trunk/py/apigen/htmlgen.py ============================================================================== --- py/trunk/py/apigen/htmlgen.py (original) +++ py/trunk/py/apigen/htmlgen.py Wed Feb 14 12:02:16 2007 @@ -170,15 +170,18 @@ return default return relpath -_rev = -1 -def get_package_revision(packageroot): - global _rev - if _rev == -1: - _rev = None +def get_package_revision(packageroot, _revcache={}): + try: + rev = _revcache[packageroot] + except KeyError: wc = py.path.svnwc(packageroot) + rev = None if wc.check(versioned=True): - _rev = py.path.svnwc(packageroot).info().rev - return _rev + rev = py.path.svnwc(packageroot).info().rev + _revcache[packageroot] = rev + if packageroot.basename == "py": + assert rev is not None + return rev # the PageBuilder classes take care of producing the docs (using the stuff # above) From hpk at codespeak.net Wed Feb 14 12:03:15 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 14 Feb 2007 12:03:15 +0100 (CET) Subject: [py-svn] r38798 - py/dist Message-ID: <20070214110315.395DA100A8@code0.codespeak.net> Author: hpk Date: Wed Feb 14 12:03:13 2007 New Revision: 38798 Added: py/dist/ - copied from r38797, py/trunk/ Log: remerging dist, hopefully the last time From hpk at codespeak.net Wed Feb 14 12:10:42 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 14 Feb 2007 12:10:42 +0100 (CET) Subject: [py-svn] r38799 - py/trunk/py Message-ID: <20070214111042.A2ECC10081@code0.codespeak.net> Author: hpk Date: Wed Feb 14 12:10:40 2007 New Revision: 38799 Modified: py/trunk/py/__init__.py Log: bumping version to 0.9.0 (trying :) Modified: py/trunk/py/__init__.py ============================================================================== --- py/trunk/py/__init__.py (original) +++ py/trunk/py/__init__.py Wed Feb 14 12:10:40 2007 @@ -1,11 +1,11 @@ """ -the py lib is a development support library featuring -py.test, an interactive testing tool which supports -unit-testing with practically no boilerplate. + the py lib is a development support library featuring + py.test, ad-hoc distributed execution, micro-threads + and svn abstractions. """ from initpkg import initpkg -version = "0.9.0-beta" +version = "0.9.0" initpkg(__name__, description = "py lib: agile development and test support library", From hpk at codespeak.net Wed Feb 14 12:11:00 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 14 Feb 2007 12:11:00 +0100 (CET) Subject: [py-svn] r38800 - py/dist Message-ID: <20070214111100.E43B61008D@code0.codespeak.net> Author: hpk Date: Wed Feb 14 12:11:00 2007 New Revision: 38800 Added: py/dist/ - copied from r38799, py/trunk/ Log: merging dist for 0.9.0 (2nd try) From hpk at codespeak.net Wed Feb 14 12:16:05 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 14 Feb 2007 12:16:05 +0100 (CET) Subject: [py-svn] r38801 - py/trunk/py/doc Message-ID: <20070214111605.3B85E10081@code0.codespeak.net> Author: hpk Date: Wed Feb 14 12:16:03 2007 New Revision: 38801 Modified: py/trunk/py/doc/download.txt Log: fixing the download page for prospected links Modified: py/trunk/py/doc/download.txt ============================================================================== --- py/trunk/py/doc/download.txt (original) +++ py/trunk/py/doc/download.txt Wed Feb 14 12:16:03 2007 @@ -9,11 +9,13 @@ The latest public release: - `download py-0.9.0-beta.tar.gz`_ - `download py-0.9.0-beta.zip`_ - -.. _`download py-0.9.0-beta.tar.gz`: http://codespeak.net/download/py/py-0.9.0-beta.tar.gz -.. _`download py-0.9.0-beta.zip`: http://codespeak.net/download/py/py-0.9.0-beta.zip + `download py-0.9.0.tar.gz`_ + `download py-0.9.0.zip`_ + `download py-0.9.0.exe`_ + +.. _`download py-0.9.0.tar.gz`: http://codespeak.net/download/py/py-0.9.0.tar.gz +.. _`download py-0.9.0.zip`: http://codespeak.net/download/py/py-0.9.0.zip +.. _`download py-0.9.0.exe`: http://codespeak.net/download/py/py-0.9.0.zip The py lib can be `globally installed via setup.py`_ or `used locally`_. From hpk at codespeak.net Wed Feb 14 12:16:23 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 14 Feb 2007 12:16:23 +0100 (CET) Subject: [py-svn] r38802 - py/dist Message-ID: <20070214111623.0857A1008D@code0.codespeak.net> Author: hpk Date: Wed Feb 14 12:16:22 2007 New Revision: 38802 Added: py/dist/ - copied from r38801, py/trunk/ Log: 3rd try for 0.9.0 From hpk at codespeak.net Wed Feb 14 12:49:13 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 14 Feb 2007 12:49:13 +0100 (CET) Subject: [py-svn] r38805 - py/trunk/py/doc Message-ID: <20070214114913.3BD7410081@code0.codespeak.net> Author: hpk Date: Wed Feb 14 12:49:11 2007 New Revision: 38805 Modified: py/trunk/py/doc/release-0.9.0.txt Log: be more careful about win32 supporting claims Modified: py/trunk/py/doc/release-0.9.0.txt ============================================================================== --- py/trunk/py/doc/release-0.9.0.txt (original) +++ py/trunk/py/doc/release-0.9.0.txt Wed Feb 14 12:49:11 2007 @@ -11,7 +11,7 @@ * py.magic.greenlet: micro-threads on standard CPython ("stackless-light") * py.path: path abstractions over local and subversion files * rich documentation of py's exported API -* tested against Win32, Linux and OSX, python 2.3-2.5 +* tested against Linux, OSX and partly against Win32, python 2.3-2.5 All these features and their API have extensive documentation, generated with the new "apigen", which we intend to make accessible From hpk at codespeak.net Wed Feb 14 13:13:40 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 14 Feb 2007 13:13:40 +0100 (CET) Subject: [py-svn] r38808 - py/trunk/py/misc Message-ID: <20070214121340.B95F61007C@code0.codespeak.net> Author: hpk Date: Wed Feb 14 13:13:39 2007 New Revision: 38808 Modified: py/trunk/py/misc/_dist.py py/trunk/py/misc/buildcmodule.py Log: trying to be more ignorant about missing abilities to remove c-extension modules (they might be in site-packages) and adding a hack to make sure that the greenlet module gets build ahead of the setup install. Modified: py/trunk/py/misc/_dist.py ============================================================================== --- py/trunk/py/misc/_dist.py (original) +++ py/trunk/py/misc/_dist.py Wed Feb 14 13:13:39 2007 @@ -135,6 +135,12 @@ def setup(pkg, **kw): """ invoke distutils on a given package. """ + print "precompiling greenlet module" + try: + x = py.magic.greenlet() + except ImportError: + print "could not precompile greenlet module, skipping" + params = Params(pkg) #dump(params) source = getattr(pkg, '__package__', pkg) Modified: py/trunk/py/misc/buildcmodule.py ============================================================================== --- py/trunk/py/misc/buildcmodule.py (original) +++ py/trunk/py/misc/buildcmodule.py Wed Feb 14 13:13:39 2007 @@ -31,38 +31,42 @@ raise ImportError, "cannot find the file name suffix of C ext modules" lib = dirpath.join(modname+ext) - # argl! we need better "build"-locations alltogether! + # XXX argl! we need better "build"-locations alltogether! if lib.check(): - lib.remove() + try: + lib.remove() + except EnvironmentError: + pass # XXX we just use the existing version, bah - c = py.io.StdCaptureFD() - try: + if not lib.check(): + c = py.io.StdCaptureFD() try: - saved_environ = os.environ.items() try: - lastdir = dirpath.chdir() + saved_environ = os.environ.items() try: - setup( - name = "pylibmodules", - ext_modules=[ - Extension(modname, [str(cfile)]) - ], - script_name = 'setup.py', - script_args = ['-q', 'build_ext', '--inplace'] - #script_args = ['build_ext', '--inplace'] - ) + lastdir = dirpath.chdir() + try: + setup( + name = "pylibmodules", + ext_modules=[ + Extension(modname, [str(cfile)]) + ], + script_name = 'setup.py', + script_args = ['-q', 'build_ext', '--inplace'] + #script_args = ['build_ext', '--inplace'] + ) + finally: + lastdir.chdir() finally: - lastdir.chdir() + for key, value in saved_environ: + if os.environ.get(key) != value: + os.environ[key] = value finally: - for key, value in saved_environ: - if os.environ.get(key) != value: - os.environ[key] = value - finally: - foutput, foutput = c.done() - except KeyboardInterrupt: - raise - except SystemExit, e: - raise RuntimeError("cannot compile %s: %s\n%s" % (cfile, e, + foutput, foutput = c.done() + except KeyboardInterrupt: + raise + except SystemExit, e: + raise RuntimeError("cannot compile %s: %s\n%s" % (cfile, e, foutput.read())) # XXX do we need to do some check on fout/ferr? # XXX not a nice way to import a module From hpk at codespeak.net Wed Feb 14 13:15:21 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 14 Feb 2007 13:15:21 +0100 (CET) Subject: [py-svn] r38810 - py/trunk/py/misc Message-ID: <20070214121521.6D83A1007C@code0.codespeak.net> Author: hpk Date: Wed Feb 14 13:15:20 2007 New Revision: 38810 Modified: py/trunk/py/misc/_dist.py Log: being yet more ignorant about build problems of c-extensions Modified: py/trunk/py/misc/_dist.py ============================================================================== --- py/trunk/py/misc/_dist.py (original) +++ py/trunk/py/misc/_dist.py Wed Feb 14 13:15:20 2007 @@ -138,7 +138,7 @@ print "precompiling greenlet module" try: x = py.magic.greenlet() - except ImportError: + except (RuntimeError, ImportError): print "could not precompile greenlet module, skipping" params = Params(pkg) From hpk at codespeak.net Wed Feb 14 13:18:05 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 14 Feb 2007 13:18:05 +0100 (CET) Subject: [py-svn] r38811 - py/dist Message-ID: <20070214121805.BB9E21007C@code0.codespeak.net> Author: hpk Date: Wed Feb 14 13:18:04 2007 New Revision: 38811 Added: py/dist/ - copied from r38810, py/trunk/ Log: 4th try From hpk at codespeak.net Wed Feb 14 13:24:48 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 14 Feb 2007 13:24:48 +0100 (CET) Subject: [py-svn] r38812 - py/trunk/py/doc Message-ID: <20070214122448.DE4AD10082@code0.codespeak.net> Author: hpk Date: Wed Feb 14 13:24:47 2007 New Revision: 38812 Modified: py/trunk/py/doc/download.txt Log: remove the exe for now and warn about missing greenlet support for win32 Modified: py/trunk/py/doc/download.txt ============================================================================== --- py/trunk/py/doc/download.txt (original) +++ py/trunk/py/doc/download.txt Wed Feb 14 13:24:47 2007 @@ -11,15 +11,16 @@ `download py-0.9.0.tar.gz`_ `download py-0.9.0.zip`_ - `download py-0.9.0.exe`_ .. _`download py-0.9.0.tar.gz`: http://codespeak.net/download/py/py-0.9.0.tar.gz .. _`download py-0.9.0.zip`: http://codespeak.net/download/py/py-0.9.0.zip -.. _`download py-0.9.0.exe`: http://codespeak.net/download/py/py-0.9.0.zip The py lib can be `globally installed via setup.py`_ -or `used locally`_. +or `used locally`_. +WARNING: win32 there is no pre-packaged c-extension +module (greenlet) yet and thus greenlets will not work +out of the box. Getting (and updating) via subversion -------------------------------------------- From fijal at codespeak.net Wed Feb 14 13:28:40 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Wed, 14 Feb 2007 13:28:40 +0100 (CET) Subject: [py-svn] r38815 - py/trunk/py/doc Message-ID: <20070214122840.857D710086@code0.codespeak.net> Author: fijal Date: Wed Feb 14 13:28:38 2007 New Revision: 38815 Modified: py/trunk/py/doc/io.txt Log: Be at least consistent within one file Modified: py/trunk/py/doc/io.txt ============================================================================== --- py/trunk/py/doc/io.txt (original) +++ py/trunk/py/doc/io.txt Wed Feb 14 13:28:38 2007 @@ -24,6 +24,7 @@ True For calling functions you may use a shortcut: + >>> import py >>> def f(): print "hello" >>> res, out, err = py.io.StdCapture.call(f) From lene at codespeak.net Wed Feb 14 13:32:27 2007 From: lene at codespeak.net (lene at codespeak.net) Date: Wed, 14 Feb 2007 13:32:27 +0100 (CET) Subject: [py-svn] r38816 - py/trunk/py/doc Message-ID: <20070214123227.67CB010081@code0.codespeak.net> Author: lene Date: Wed Feb 14 13:32:24 2007 New Revision: 38816 Modified: py/trunk/py/doc/index.txt Log: tried to fix sentences and singular/plural Modified: py/trunk/py/doc/index.txt ============================================================================== --- py/trunk/py/doc/index.txt (original) +++ py/trunk/py/doc/index.txt Wed Feb 14 13:32:24 2007 @@ -10,9 +10,9 @@ Main tools and API ---------------------- -`py.test`_ introduces to the **py.test** testing utility +`py.test`_ introduces to the **py.test** testing utility. -`py.execnet`_ distribute programs across the net +`py.execnet`_ distributes programs across the net. `py.magic.greenlet`_: micro-threads (lightweight in-process concurrent programming) @@ -29,18 +29,18 @@ `py.xml`_ for generating in-memory xml/html object trees -`py.io`_ Helper Classes for Capturing of Input/Output +`py.io`_: Helper Classes for Capturing of Input/Output -`py.log`_ an alpha document about the ad-hoc logging facilities +`py.log`_: an alpha document about the ad-hoc logging facilities -`miscellaneous features`_ describes some small but nice py lib features +`miscellaneous features`_ describes some small but nice py lib features. Background and Motivation information ------------------------------------------- `future`_ handles development visions and plans for the near future. -`why what how py?`_, describing motivation and background of the py lib +`why what how py?`_, describing motivation and background of the py lib. .. _`download and installation`: download.html .. _`py-dev at codespeak net`: http://codespeak.net/mailman/listinfo/py-dev From cfbolz at codespeak.net Wed Feb 14 13:32:38 2007 From: cfbolz at codespeak.net (cfbolz at codespeak.net) Date: Wed, 14 Feb 2007 13:32:38 +0100 (CET) Subject: [py-svn] r38817 - py/trunk/py/doc Message-ID: <20070214123238.6313210083@code0.codespeak.net> Author: cfbolz Date: Wed Feb 14 13:32:37 2007 New Revision: 38817 Modified: py/trunk/py/doc/coding-style.txt Log: fix typo, make the promise less definite (it's ages old) Modified: py/trunk/py/doc/coding-style.txt ============================================================================== --- py/trunk/py/doc/coding-style.txt (original) +++ py/trunk/py/doc/coding-style.txt Wed Feb 14 13:32:37 2007 @@ -47,7 +47,7 @@ - bug fixes should be encoded in a test before being fixed. - write telling log messages because several people - will read your diffs, an we will have a search facility + will read your diffs, and we plan to have a search facility over the py lib's subversion repository. - if you add ``.txt`` or ``.py`` files to the repository then From hpk at codespeak.net Wed Feb 14 13:34:19 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 14 Feb 2007 13:34:19 +0100 (CET) Subject: [py-svn] r38818 - py/trunk/py/doc Message-ID: <20070214123419.5897B10081@code0.codespeak.net> Author: hpk Date: Wed Feb 14 13:34:17 2007 New Revision: 38818 Modified: py/trunk/py/doc/index.txt Log: add release 0.9.0 announcement link to index.txt Modified: py/trunk/py/doc/index.txt ============================================================================== --- py/trunk/py/doc/index.txt (original) +++ py/trunk/py/doc/index.txt Wed Feb 14 13:34:17 2007 @@ -7,6 +7,8 @@ `Download and Installation`_ +`0.9.0 release announcement`_ + Main tools and API ---------------------- @@ -57,4 +59,4 @@ .. _`Why What how py?`: why_py.html .. _`future`: future.html .. _`miscellaneous features`: misc.html - +.. _`0.9.0 release announcement`: release-0.9.0.html From hpk at codespeak.net Wed Feb 14 13:44:53 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 14 Feb 2007 13:44:53 +0100 (CET) Subject: [py-svn] r38819 - py/trunk/py/apigen/testing Message-ID: <20070214124453.AC56010081@code0.codespeak.net> Author: hpk Date: Wed Feb 14 13:44:52 2007 New Revision: 38819 Modified: py/trunk/py/apigen/testing/test_apigen_functional.py Log: skip apigen on win32 Modified: py/trunk/py/apigen/testing/test_apigen_functional.py ============================================================================== --- py/trunk/py/apigen/testing/test_apigen_functional.py (original) +++ py/trunk/py/apigen/testing/test_apigen_functional.py Wed Feb 14 13:44:52 2007 @@ -6,6 +6,9 @@ import py from py.__.apigen import apigen +def setup_module(mod): + if py.std.sys.platform == "win32": + py.test.skip("not supported with win32 yet") def setup_fs_project(name): temp = py.test.ensuretemp(name) From hpk at codespeak.net Wed Feb 14 13:47:44 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 14 Feb 2007 13:47:44 +0100 (CET) Subject: [py-svn] r38820 - py/dist Message-ID: <20070214124744.4564B10081@code0.codespeak.net> Author: hpk Date: Wed Feb 14 13:47:43 2007 New Revision: 38820 Added: py/dist/ - copied from r38819, py/trunk/ Log: 5th try to get 0.9.0 dist From hpk at codespeak.net Wed Feb 14 13:51:08 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 14 Feb 2007 13:51:08 +0100 (CET) Subject: [py-svn] r38821 - py/trunk/py/misc Message-ID: <20070214125108.E8BE410081@code0.codespeak.net> Author: hpk Date: Wed Feb 14 13:51:08 2007 New Revision: 38821 Modified: py/trunk/py/misc/_dist.py Log: hum, only do the precompiled greenlet if we are invoked with install Modified: py/trunk/py/misc/_dist.py ============================================================================== --- py/trunk/py/misc/_dist.py (original) +++ py/trunk/py/misc/_dist.py Wed Feb 14 13:51:08 2007 @@ -135,11 +135,12 @@ def setup(pkg, **kw): """ invoke distutils on a given package. """ - print "precompiling greenlet module" - try: - x = py.magic.greenlet() - except (RuntimeError, ImportError): - print "could not precompile greenlet module, skipping" + if 'install' in sys.argv[1:]: + print "precompiling greenlet module" + try: + x = py.magic.greenlet() + except (RuntimeError, ImportError): + print "could not precompile greenlet module, skipping" params = Params(pkg) #dump(params) From hpk at codespeak.net Wed Feb 14 13:51:25 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 14 Feb 2007 13:51:25 +0100 (CET) Subject: [py-svn] r38822 - py/dist Message-ID: <20070214125125.794FA10086@code0.codespeak.net> Author: hpk Date: Wed Feb 14 13:51:24 2007 New Revision: 38822 Added: py/dist/ - copied from r38821, py/trunk/ Log: 6th try (bah) From hpk at codespeak.net Wed Feb 14 14:11:36 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 14 Feb 2007 14:11:36 +0100 (CET) Subject: [py-svn] r38824 - py/trunk/py/apigen/tracer/testing Message-ID: <20070214131136.9CB0610089@code0.codespeak.net> Author: hpk Date: Wed Feb 14 14:11:34 2007 New Revision: 38824 Modified: py/trunk/py/apigen/tracer/testing/test_docgen.py Log: skip these tests on win32 Modified: py/trunk/py/apigen/tracer/testing/test_docgen.py ============================================================================== --- py/trunk/py/apigen/tracer/testing/test_docgen.py (original) +++ py/trunk/py/apigen/tracer/testing/test_docgen.py Wed Feb 14 14:11:34 2007 @@ -13,13 +13,10 @@ from py.__.apigen.tracer.description import FunctionDesc from py.__.apigen.tracer import model from py.__.apigen.tracer.permastore import PermaDocStorage -# from pypy.annotation import model -#except ImportError, s: -# py.test.skip("Cannot import: %s" % str(s)) - -#def setup_module(mod): -# data_path = py.path.local(mod.__file__).dirpath().join("data") -# sys.path.insert(0, str(data_path)) + +def setup_module(mod): + if py.std.sys.platform == "win32": + py.test.skip("tracing on win32 not supported") # XXX: Perma doc storage disabled a bit From hpk at codespeak.net Wed Feb 14 14:11:53 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 14 Feb 2007 14:11:53 +0100 (CET) Subject: [py-svn] r38825 - py/dist Message-ID: <20070214131153.E77A510094@code0.codespeak.net> Author: hpk Date: Wed Feb 14 14:11:53 2007 New Revision: 38825 Added: py/dist/ - copied from r38824, py/trunk/ Log: 7th try From arigo at codespeak.net Wed Feb 14 14:57:47 2007 From: arigo at codespeak.net (arigo at codespeak.net) Date: Wed, 14 Feb 2007 14:57:47 +0100 (CET) Subject: [py-svn] r38828 - py/trunk/py/doc Message-ID: <20070214135747.936D710082@code0.codespeak.net> Author: arigo Date: Wed Feb 14 14:57:46 2007 New Revision: 38828 Modified: py/trunk/py/doc/confrest.py py/trunk/py/doc/conftest.py Log: Try harder to get the logic right to avoid regenerating html files for unmodified txts. Modified: py/trunk/py/doc/confrest.py ============================================================================== --- py/trunk/py/doc/confrest.py (original) +++ py/trunk/py/doc/confrest.py Wed Feb 14 14:57:46 2007 @@ -126,12 +126,16 @@ def get_docpath(self): return get_docpath() + def get_htmloutputpath(self, txtpath): + docpath = self.get_docpath() + reloutputpath = txtpath.new(ext='.html').relto(self.mydir) + return docpath.join(reloutputpath) + def process(self, txtpath): encoding = self.encoding content = self.get_content(txtpath, encoding) docpath = self.get_docpath() - reloutputpath = txtpath.new(ext='.html').relto(self.mydir) - outputpath = docpath.join(reloutputpath) + outputpath = self.get_htmloutputpath(txtpath) stylesheet = self.stylesheet if isinstance(self.stylesheet, py.path.local): Modified: py/trunk/py/doc/conftest.py ============================================================================== --- py/trunk/py/doc/conftest.py (original) +++ py/trunk/py/doc/conftest.py Wed Feb 14 14:57:46 2007 @@ -73,7 +73,6 @@ localpath = path if hasattr(path, 'localpath'): localpath = path.localpath - _checkskip(localpath) checkdocutils() import docutils.utils @@ -82,11 +81,14 @@ for x in cur.parts(reverse=True): confrest = x.dirpath('confrest.py') if confrest.check(file=1): - confrest = confrest.pyimport() - confrest.Project().process(path) + confrest = confrest.pyimport() + project = confrest.Project() + _checkskip(path, project.get_htmloutputpath(path)) + project.process(path) break else: # defer to default processor + _checkskip(path) rest.process(path) except KeyboardInterrupt: raise @@ -94,10 +96,10 @@ # we assume docutils printed info on stdout py.test.fail("docutils processing failed, see captured stderr") -def _checkskip(lpath): +def _checkskip(lpath, htmlpath=None): if not option.forcegen: if lpath.ext == '.txt': - htmlpath = get_docpath().join(lpath.new(ext='.html').relto(mypath)) + htmlpath = htmlpath or lpath.new(ext='.html') if htmlpath.check(file=1) and htmlpath.mtime() >= lpath.mtime(): py.test.skip("html file is up to date, use --forcegen to regenerate") #return [] # no need to rebuild @@ -105,7 +107,6 @@ class ReSTSyntaxTest(py.test.collect.Item): def run(self): mypath = self.fspath - _checkskip(mypath) restcheck(py.path.svnwc(mypath)) class DoctestText(py.test.collect.Item): From hpk at codespeak.net Wed Feb 14 15:07:24 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 14 Feb 2007 15:07:24 +0100 (CET) Subject: [py-svn] r38831 - py/trunk/py/doc Message-ID: <20070214140724.013DB1007B@code0.codespeak.net> Author: hpk Date: Wed Feb 14 15:07:23 2007 New Revision: 38831 Modified: py/trunk/py/doc/conftest.py Log: be more careful to not work with svnwc() paths when checking for mtimes Modified: py/trunk/py/doc/conftest.py ============================================================================== --- py/trunk/py/doc/conftest.py (original) +++ py/trunk/py/doc/conftest.py Wed Feb 14 15:07:23 2007 @@ -98,6 +98,9 @@ def _checkskip(lpath, htmlpath=None): if not option.forcegen: + lpath = py.path.local(lpath) + if htmlpath is not None: + htmlpath = py.path.local(htmlpath) if lpath.ext == '.txt': htmlpath = htmlpath or lpath.new(ext='.html') if htmlpath.check(file=1) and htmlpath.mtime() >= lpath.mtime(): From hpk at codespeak.net Wed Feb 14 15:07:56 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 14 Feb 2007 15:07:56 +0100 (CET) Subject: [py-svn] r38832 - py/dist Message-ID: <20070214140756.ABA6310081@code0.codespeak.net> Author: hpk Date: Wed Feb 14 15:07:55 2007 New Revision: 38832 Added: py/dist/ - copied from r38831, py/trunk/ Log: 8th try From hpk at codespeak.net Wed Feb 14 15:45:24 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 14 Feb 2007 15:45:24 +0100 (CET) Subject: [py-svn] r38839 - py/release/0.9.x Message-ID: <20070214144524.94A4610082@code0.codespeak.net> Author: hpk Date: Wed Feb 14 15:45:23 2007 New Revision: 38839 Added: py/release/0.9.x/ - copied from r38838, py/dist/ Log: 0.9 release and maintenance branch From hpk at codespeak.net Wed Feb 14 15:48:41 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 14 Feb 2007 15:48:41 +0100 (CET) Subject: [py-svn] r38840 - py/trunk/py/doc Message-ID: <20070214144841.66F871008D@code0.codespeak.net> Author: hpk Date: Wed Feb 14 15:48:39 2007 New Revision: 38840 Modified: py/trunk/py/doc/download.txt Log: fix links to release branch Modified: py/trunk/py/doc/download.txt ============================================================================== --- py/trunk/py/doc/download.txt (original) +++ py/trunk/py/doc/download.txt Wed Feb 14 15:48:39 2007 @@ -98,7 +98,7 @@ or the next one for following the e.g. the 0.9 release branch - http://codespeak.net/svn/py/release/py-0.9.x + http://codespeak.net/svn/py/release/0.9.x py subversion directory structure @@ -108,9 +108,6 @@ going to follow this scheme:: http://codespeak.net/ - svn/py/dist # latest stable (may or may not be a release) - svn/py/release/X.Y.Z # release tags and branches - svn/py/trunk # head development / merge point - - - + svn/py/dist # latest stable (may or may not be a release) + svn/py/release # release tags and branches + svn/py/trunk # head development / merge point From hpk at codespeak.net Wed Feb 14 15:49:19 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 14 Feb 2007 15:49:19 +0100 (CET) Subject: [py-svn] r38841 - py/dist Message-ID: <20070214144919.08DDB100A3@code0.codespeak.net> Author: hpk Date: Wed Feb 14 15:49:19 2007 New Revision: 38841 Added: py/dist/ - copied from r38840, py/trunk/ Log: 9th try (i guess now cfbolz should look to see if can make his prediction come true :) From hpk at codespeak.net Wed Feb 14 15:55:00 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 14 Feb 2007 15:55:00 +0100 (CET) Subject: [py-svn] r38842 - py/release/0.9.x Message-ID: <20070214145500.21946100A8@code0.codespeak.net> Author: hpk Date: Wed Feb 14 15:54:58 2007 New Revision: 38842 Added: py/release/0.9.x/ - copied from r38841, py/dist/ Log: subst release branch (fixed download.txt) From hpk at codespeak.net Wed Feb 14 16:41:49 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 14 Feb 2007 16:41:49 +0100 (CET) Subject: [py-svn] r38846 - py/release/0.9.0 Message-ID: <20070214154149.296B81008A@code0.codespeak.net> Author: hpk Date: Wed Feb 14 16:41:49 2007 New Revision: 38846 Added: py/release/0.9.0/ - copied from r38845, py/release/0.9.x/ Log: 0.9.0 release tag From santagada at codespeak.net Thu Feb 15 22:12:20 2007 From: santagada at codespeak.net (santagada at codespeak.net) Date: Thu, 15 Feb 2007 22:12:20 +0100 (CET) Subject: [py-svn] r38967 - py/trunk/py/doc Message-ID: <20070215211220.6552F10081@code0.codespeak.net> Author: santagada Date: Thu Feb 15 22:12:15 2007 New Revision: 38967 Modified: py/trunk/py/doc/impl-test.txt Log: spelling mistake Modified: py/trunk/py/doc/impl-test.txt ============================================================================== --- py/trunk/py/doc/impl-test.txt (original) +++ py/trunk/py/doc/impl-test.txt Thu Feb 15 22:12:15 2007 @@ -180,7 +180,7 @@ custom Collectors and Items if they are found in a local ``conftest.py`` file. -example: perform additional ReST checs +example: perform additional ReST checks ++++++++++++++++++++++++++++++++++++++ With your custom collectors or items you can completely From guido at codespeak.net Thu Feb 15 22:33:33 2007 From: guido at codespeak.net (guido at codespeak.net) Date: Thu, 15 Feb 2007 22:33:33 +0100 (CET) Subject: [py-svn] r38969 - py/trunk/py/doc Message-ID: <20070215213333.8205C1007C@code0.codespeak.net> Author: guido Date: Thu Feb 15 22:33:31 2007 New Revision: 38969 Modified: py/trunk/py/doc/impl-test.txt Log: Fixed ReST. Modified: py/trunk/py/doc/impl-test.txt ============================================================================== --- py/trunk/py/doc/impl-test.txt (original) +++ py/trunk/py/doc/impl-test.txt Thu Feb 15 22:33:31 2007 @@ -181,7 +181,7 @@ in a local ``conftest.py`` file. example: perform additional ReST checks -++++++++++++++++++++++++++++++++++++++ ++++++++++++++++++++++++++++++++++++++++ With your custom collectors or items you can completely derive from the standard way of collecting and running From xoraxax at codespeak.net Sat Feb 17 15:12:59 2007 From: xoraxax at codespeak.net (xoraxax at codespeak.net) Date: Sat, 17 Feb 2007 15:12:59 +0100 (CET) Subject: [py-svn] r39106 - py/trunk/py/path/svn Message-ID: <20070217141259.657D810167@code0.codespeak.net> Author: xoraxax Date: Sat Feb 17 15:12:56 2007 New Revision: 39106 Modified: py/trunk/py/path/svn/svncommon.py Log: Plus sign works fine for me, added to ALLOWED_CHARS in svncommon. Modified: py/trunk/py/path/svn/svncommon.py ============================================================================== --- py/trunk/py/path/svn/svncommon.py (original) +++ py/trunk/py/path/svn/svncommon.py Sat Feb 17 15:12:56 2007 @@ -5,7 +5,7 @@ import py from py.__.path import common -ALLOWED_CHARS = "_ -/\\=$.~" #add characters as necessary when tested +ALLOWED_CHARS = "_ -/\\=$.~+" #add characters as necessary when tested if sys.platform == "win32": ALLOWED_CHARS += ":" ALLOWED_CHARS_HOST = ALLOWED_CHARS + '@:' From hpk at codespeak.net Wed Feb 21 10:50:32 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 21 Feb 2007 10:50:32 +0100 (CET) Subject: [py-svn] r39256 - py/release/0.9.x/py Message-ID: <20070221095032.47ABB10162@code0.codespeak.net> Author: hpk Date: Wed Feb 21 10:50:30 2007 New Revision: 39256 Modified: py/release/0.9.x/py/__init__.py Log: bumping version number of release-branch to 0.9.1-alpha Modified: py/release/0.9.x/py/__init__.py ============================================================================== --- py/release/0.9.x/py/__init__.py (original) +++ py/release/0.9.x/py/__init__.py Wed Feb 21 10:50:30 2007 @@ -5,7 +5,7 @@ """ from initpkg import initpkg -version = "0.9.0" +version = "0.9.1-alpha" initpkg(__name__, description = "py lib: agile development and test support library", From hpk at codespeak.net Wed Feb 21 10:51:37 2007 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 21 Feb 2007 10:51:37 +0100 (CET) Subject: [py-svn] r39258 - py/release/0.9.x/py/apigen Message-ID: <20070221095137.8A7FB10169@code0.codespeak.net> Author: hpk Date: Wed Feb 21 10:51:36 2007 New Revision: 39258 Modified: py/release/0.9.x/py/apigen/htmlgen.py Log: applied suggested patch from Alexandre Modified: py/release/0.9.x/py/apigen/htmlgen.py ============================================================================== --- py/release/0.9.x/py/apigen/htmlgen.py (original) +++ py/release/0.9.x/py/apigen/htmlgen.py Wed Feb 21 10:51:36 2007 @@ -178,6 +178,8 @@ rev = None if wc.check(versioned=True): rev = py.path.svnwc(packageroot).info().rev + else: + rev = py.__package__.revision _revcache[packageroot] = rev if packageroot.basename == "py": assert rev is not None From fijal at codespeak.net Fri Feb 23 15:22:51 2007 From: fijal at codespeak.net (fijal at codespeak.net) Date: Fri, 23 Feb 2007 15:22:51 +0100 (CET) Subject: [py-svn] r39340 - in py/trunk/py/test/rsession: . testing Message-ID: <20070223142251.B6A29101A7@code0.codespeak.net> Author: fijal Date: Fri Feb 23 15:22:50 2007 New Revision: 39340 Modified: py/trunk/py/test/rsession/executor.py py/trunk/py/test/rsession/testing/test_executor.py Log: A fix and a test. Thanks Marian Shubert for reporting problem Modified: py/trunk/py/test/rsession/executor.py ============================================================================== --- py/trunk/py/test/rsession/executor.py (original) +++ py/trunk/py/test/rsession/executor.py Fri Feb 23 15:22:50 2007 @@ -40,7 +40,7 @@ raise except: e = sys.exc_info()[1] - if isinstance(e, Failed): + if isinstance(e, Failed) and e.excinfo: excinfo = e.excinfo else: excinfo = py.code.ExceptionInfo() Modified: py/trunk/py/test/rsession/testing/test_executor.py ============================================================================== --- py/trunk/py/test/rsession/testing/test_executor.py (original) +++ py/trunk/py/test/rsession/testing/test_executor.py Fri Feb 23 15:22:50 2007 @@ -37,6 +37,10 @@ def run(self): raise Failed(excinfo="3") +class ItemTestFailingExplicitEmpty(Item): + def run(self): + py.test.raises(ValueError, lambda : 123) + class TestExecutor(BasicRsessionTest): def test_run_executor(self): ex = RunExecutor(ItemTestPassing("pass", self.config), config=self.config) @@ -158,3 +162,10 @@ outcome = ex.execute() assert not outcome.passed assert outcome.excinfo == "3" + + def test_executor_explicit_Faile_no_excinfo(self): + ex = RunExecutor(ItemTestFailingExplicitEmpty("failexx", self.config), + config=self.config) + outcome = ex.execute() + assert not outcome.passed +