From py-svn at codespeak.net Sat May 2 09:02:23 2009 From: py-svn at codespeak.net (Rufus Yunt) Date: Sat, 2 May 2009 10:02:23 +0300 Subject: [py-svn] Buffet's words about dollar Message-ID: <20090502070513.D5350168504@codespeak.net> Drink one and get up to 6 hours of pure pleasure with her http://wutazbuc.wobboyav.cn/ -------------- next part -------------- An HTML attachment was scrubbed... URL: From py-svn at codespeak.net Tue May 5 00:34:57 2009 From: py-svn at codespeak.net (Lynna Dorothy) Date: Tue, 5 May 2009 00:34:57 +0200 (CEST) Subject: [py-svn] Discount 78% from Dr. Lynna Dorothy Message-ID: <20090504223457.DD0511684C1@codespeak.net> An HTML attachment was scrubbed... URL: From py-svn at codespeak.net Thu May 7 13:53:47 2009 From: py-svn at codespeak.net (Ophelia Nesby) Date: Thu, 7 May 2009 13:53:47 +0200 (CEST) Subject: [py-svn] Personal discount 77%. Dr. Ophelia Message-ID: <20090507115347.612A6169E3B@codespeak.net> An HTML attachment was scrubbed... URL: From hpk at codespeak.net Thu May 7 16:22:04 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 7 May 2009 16:22:04 +0200 (CEST) Subject: [py-svn] r65139 - in py/trunk: doc py py/builtin/testing py/cmdline py/log py/log/testing py/magic py/magic/testing py/test py/test/plugin py/test/testing Message-ID: <20090507142204.3181E169E12@codespeak.net> Author: hpk Date: Thu May 7 16:22:01 2009 New Revision: 65139 Added: py/trunk/py/test/plugin/pytest_recwarn.py Modified: py/trunk/doc/conftest.py py/trunk/py/builtin/testing/test_exception.py py/trunk/py/cmdline/pycleanup.py py/trunk/py/conftest.py py/trunk/py/log/testing/test_warning.py py/trunk/py/log/warning.py py/trunk/py/magic/exprinfo.py py/trunk/py/magic/testing/test_exprinfo.py py/trunk/py/test/collect.py py/trunk/py/test/defaultconftest.py py/trunk/py/test/plugin/pytest_default.py py/trunk/py/test/plugin/pytest_pytester.py py/trunk/py/test/plugin/pytest_restdoc.py py/trunk/py/test/plugin/pytest_resultdb.py py/trunk/py/test/plugin/pytest_terminal.py py/trunk/py/test/pycollect.py py/trunk/py/test/runner.py py/trunk/py/test/testing/acceptance_test.py py/trunk/py/test/testing/test_collect.py py/trunk/py/test/testing/test_deprecated_api.py py/trunk/py/test/testing/test_funcargs.py py/trunk/py/test/testing/test_pycollect.py Log: merging from mercurial py-trunk * fix issue 7 (delay in test writing) * new recwarn plugin (for cool assertions about warnings) * have items provide pure metainfo() and leave reporting to TerminalReporter * funcargs are now instantiated during setup phase * extend py.cleanup to supply a list of extensions to clean * python2.6 fixes Modified: py/trunk/doc/conftest.py ============================================================================== --- py/trunk/doc/conftest.py (original) +++ py/trunk/doc/conftest.py Thu May 7 16:22:01 2009 @@ -1,5 +1,5 @@ #XXX make work: excludedirs = ['_build'] import py -py.test.importorskip("pygments") +#py.test.importorskip("pygments") pytest_plugins = ['pytest_restdoc'] rsyncdirs = ['.'] Modified: py/trunk/py/builtin/testing/test_exception.py ============================================================================== --- py/trunk/py/builtin/testing/test_exception.py (original) +++ py/trunk/py/builtin/testing/test_exception.py Thu May 7 16:22:01 2009 @@ -15,4 +15,4 @@ def test_GeneratorExit(): assert py.builtin.GeneratorExit.__module__ == 'exceptions' - assert issubclass(py.builtin.GeneratorExit, Exception) + assert issubclass(py.builtin.GeneratorExit, py.builtin.BaseException) Modified: py/trunk/py/cmdline/pycleanup.py ============================================================================== --- py/trunk/py/cmdline/pycleanup.py (original) +++ py/trunk/py/cmdline/pycleanup.py Thu May 7 16:22:01 2009 @@ -11,11 +11,26 @@ def main(): parser = py.compat.optparse.OptionParser(usage=__doc__) + parser.add_option("-e", "--remove", dest="ext", default=".pyc", action="store", + help="remove files with the given comma-separated list of extensions" + ) + parser.add_option("-n", "--dryrun", dest="dryrun", default=False, + action="store_true", + help="display would-be-removed filenames" + ) (options, args) = parser.parse_args() if not args: args = ["."] + ext = options.ext.split(",") + def shouldremove(p): + return p.ext in ext + for arg in args: path = py.path.local(arg) - print "cleaning path", path - for x in path.visit('*.pyc', lambda x: x.check(dotfile=0, link=0)): - x.remove() + print "cleaning path", path, "of extensions", ext + for x in path.visit(shouldremove, lambda x: x.check(dotfile=0, link=0)): + if options.dryrun: + print "would remove", x + else: + print "removing", x + x.remove() Modified: py/trunk/py/conftest.py ============================================================================== --- py/trunk/py/conftest.py (original) +++ py/trunk/py/conftest.py Thu May 7 16:22:01 2009 @@ -1,7 +1,6 @@ pytest_plugins = '_pytest doctest pytester'.split() rsyncdirs = ['../doc'] -rsyncignore = ['c-extension/greenlet/build'] import py class PylibTestconfigPlugin: Modified: py/trunk/py/log/testing/test_warning.py ============================================================================== --- py/trunk/py/log/testing/test_warning.py (original) +++ py/trunk/py/log/testing/test_warning.py Thu May 7 16:22:01 2009 @@ -26,3 +26,15 @@ lno = test_stacklevel.func_code.co_firstlineno + 6 warning = str(err) assert warning.find(":%s" % lno) != -1 + +def test_function(): + capture = py.io.StdCapture() + py.log._apiwarn("x.y.z", "something", function=test_function) + out, err = capture.reset() + print "out", out + print "err", err + assert err.find("x.y.z") != -1 + lno = test_function.func_code.co_firstlineno + exp = "%s:%s" % (mypath, lno) + assert err.find(exp) != -1 + Modified: py/trunk/py/log/warning.py ============================================================================== --- py/trunk/py/log/warning.py (original) +++ py/trunk/py/log/warning.py Thu May 7 16:22:01 2009 @@ -10,26 +10,30 @@ def __str__(self): return self.msg -def _apiwarn(startversion, msg, stacklevel=1): +def _apiwarn(startversion, msg, stacklevel=1, function=None): # below is mostly COPIED from python2.4/warnings.py's def warn() # Get context information msg = "%s (since version %s)" %(msg, startversion) - warn(msg, stacklevel=stacklevel+1) + warn(msg, stacklevel=stacklevel+1, function=function) -def warn(msg, stacklevel=1): - try: - caller = sys._getframe(stacklevel) - except ValueError: - globals = sys.__dict__ - lineno = 1 +def warn(msg, stacklevel=1, function=None): + if function is not None: + filename = py.std.inspect.getfile(function) + lineno = function.func_code.co_firstlineno else: - globals = caller.f_globals - lineno = caller.f_lineno - if '__name__' in globals: - module = globals['__name__'] - else: - module = "" - filename = globals.get('__file__') + try: + caller = sys._getframe(stacklevel) + except ValueError: + globals = sys.__dict__ + lineno = 1 + else: + globals = caller.f_globals + lineno = caller.f_lineno + if '__name__' in globals: + module = globals['__name__'] + else: + module = "" + filename = globals.get('__file__') if filename: fnl = filename.lower() if fnl.endswith(".pyc") or fnl.endswith(".pyo"): Modified: py/trunk/py/magic/exprinfo.py ============================================================================== --- py/trunk/py/magic/exprinfo.py (original) +++ py/trunk/py/magic/exprinfo.py Thu May 7 16:22:01 2009 @@ -428,7 +428,9 @@ import traceback traceback.print_exc() if should_fail: - return "(inconsistently failed then succeeded)" + return ("(assertion failed, but when it was re-run for " + "printing intermediate values, it did not fail. Suggestions: " + "compute assert expression before the assert or use --nomagic)") else: return None Modified: py/trunk/py/magic/testing/test_exprinfo.py ============================================================================== --- py/trunk/py/magic/testing/test_exprinfo.py (original) +++ py/trunk/py/magic/testing/test_exprinfo.py Thu May 7 16:22:01 2009 @@ -119,3 +119,15 @@ pass else: raise AssertionError, "ex %s didn't pass through" %(exstr, ) + +def test_inconsistent_assert_result(testdir): + p = testdir.makepyfile(""" + def test_func(): + def f(l=[1,0]): + return l.pop() + assert f() + """) + result = testdir.runpytest(p) + s = result.stdout.str() + assert s.find("re-run") != -1 + Modified: py/trunk/py/test/collect.py ============================================================================== --- py/trunk/py/test/collect.py (original) +++ py/trunk/py/test/collect.py Thu May 7 16:22:01 2009 @@ -12,31 +12,6 @@ return self.config.getvalue(name, self.fspath) return property(fget) -class ReprMetaInfo(object): - def __init__(self, fspath=None, lineno=None, modpath=None): - self.fspath = fspath - self.lineno = lineno - self.modpath = modpath - - def verboseline(self, basedir=None): - params = self.__dict__.copy() - if self.fspath: - if basedir is not None: - params['fspath'] = basedir.bestrelpath(self.fspath) - if self.lineno is not None: - params['lineno'] = self.lineno + 1 - - if self.fspath and self.lineno and self.modpath: - line = "%(fspath)s:%(lineno)s: %(modpath)s" - elif self.fspath and self.modpath: - line = "%(fspath)s: %(modpath)s" - elif self.fspath and self.lineno: - line = "%(fspath)s:%(lineno)s" - else: - line = "[nometainfo]" - return line % params - - class Node(object): """ base class for Nodes in the collection tree. Collector nodes have children and @@ -50,7 +25,6 @@ - configuration/options for setup/teardown stdout/stderr capturing and execution of test items """ - ReprMetaInfo = ReprMetaInfo def __init__(self, name, parent=None, config=None): self.name = name self.parent = parent @@ -347,7 +321,7 @@ setattr(self, attrname, True) method = getattr(self.__class__, 'run', None) if method is not None and method != Collector.run: - warnoldcollect() + warnoldcollect(function=method) names = self.run() return filter(None, [self.join(name) for name in names]) @@ -356,14 +330,12 @@ You can return an empty list. Callers of this method must take care to catch exceptions properly. """ - warnoldcollect() return [colitem.name for colitem in self._memocollect()] def join(self, name): """ DEPRECATED: return a child collector or item for the given name. If the return value is None there is no such child. """ - warnoldcollect() return self.collect_by_name(name) class FSCollector(Collector): @@ -451,40 +423,33 @@ """ a basic test item. """ def _deprecated_testexecution(self): if self.__class__.run != Item.run: - warnoldtestrun() - self.run() - return True + warnoldtestrun(function=self.run) elif self.__class__.execute != Item.execute: - warnoldtestrun() - self.execute(self.obj, *self._args) - return True + warnoldtestrun(function=self.execute) + else: + return False + self.run() + return True def run(self): - warnoldtestrun() + """ deprecated, here because subclasses might call it. """ return self.execute(self.obj, *self._args) def execute(self, obj, *args): - warnoldtestrun() + """ deprecated, here because subclasses might call it. """ return obj(*args) - def repr_metainfo(self): - try: - return self.ReprMetaInfo(self.fspath, modpath=self.__class__.__name__) - except AttributeError: - code = py.code.Code(self.execute) - return self.ReprMetaInfo(code.path, code.firstlineno) - - def runtest(self): - """ execute this test item.""" + def metainfo(self): + return self.fspath, None, "" -def warnoldcollect(): +def warnoldcollect(function=None): py.log._apiwarn("1.0", "implement collector.collect() instead of " "collector.run() and collector.join()", - stacklevel=2) + stacklevel=2, function=function) -def warnoldtestrun(): +def warnoldtestrun(function=None): py.log._apiwarn("1.0", "implement item.runtest() instead of " "item.run() and item.execute()", - stacklevel=2) + stacklevel=2, function=function) Modified: py/trunk/py/test/defaultconftest.py ============================================================================== --- py/trunk/py/test/defaultconftest.py (original) +++ py/trunk/py/test/defaultconftest.py Thu May 7 16:22:01 2009 @@ -10,5 +10,5 @@ Function = py.test.collect.Function Instance = py.test.collect.Instance -pytest_plugins = "default terminal xfail tmpdir execnetcleanup monkeypatch pdb".split() +pytest_plugins = "default terminal xfail tmpdir execnetcleanup monkeypatch recwarn pdb".split() Modified: py/trunk/py/test/plugin/pytest_default.py ============================================================================== --- py/trunk/py/test/plugin/pytest_default.py (original) +++ py/trunk/py/test/plugin/pytest_default.py Thu May 7 16:22:01 2009 @@ -191,10 +191,12 @@ def test_plugin_specify(testdir): testdir.chdir() - config = testdir.parseconfig("-p", "nqweotexistent") - py.test.raises(ImportError, - "config.pluginmanager.do_configure(config)" - ) + config = py.test.raises(ImportError, """ + testdir.parseconfig("-p", "nqweotexistent") + """) + #py.test.raises(ImportError, + # "config.pluginmanager.do_configure(config)" + #) def test_plugin_already_exists(testdir): config = testdir.parseconfig("-p", "default") Modified: py/trunk/py/test/plugin/pytest_pytester.py ============================================================================== --- py/trunk/py/test/plugin/pytest_pytester.py (original) +++ py/trunk/py/test/plugin/pytest_pytester.py Thu May 7 16:22:01 2009 @@ -3,6 +3,7 @@ """ import py +import os import inspect from py.__.test import runner from py.__.test.config import Config as pytestConfig @@ -222,6 +223,9 @@ def popen(self, cmdargs, stdout, stderr, **kw): if not hasattr(py.std, 'subprocess'): py.test.skip("no subprocess module") + env = os.environ.copy() + env['PYTHONPATH'] = "%s:%s" % (os.getcwd(), env['PYTHONPATH']) + kw['env'] = env return py.std.subprocess.Popen(cmdargs, stdout=stdout, stderr=stderr, **kw) def run(self, *cmdargs): @@ -250,10 +254,14 @@ return RunResult(ret, out, err) def runpybin(self, scriptname, *args): + fullargs = self._getpybinargs(scriptname) + args + return self.run(*fullargs) + + def _getpybinargs(self, scriptname): bindir = py.path.local(py.__file__).dirpath("bin") script = bindir.join(scriptname) assert script.check() - return self.run(py.std.sys.executable, script, *args) + return py.std.sys.executable, script def runpytest(self, *args): p = py.path.local.make_numbered_dir(prefix="runpytest-", @@ -261,6 +269,15 @@ args = ('--basetemp=%s' % p, ) + args return self.runpybin("py.test", *args) + def spawn_pytest(self, string, expect_timeout=10.0): + pexpect = py.test.importorskip("pexpect", "2.3") + basetemp = self.tmpdir.mkdir("pexpect") + invoke = "%s %s" % self._getpybinargs("py.test") + cmd = "%s --basetemp=%s %s" % (invoke, basetemp, string) + child = pexpect.spawn(cmd, logfile=basetemp.join("spawn.out").open("w")) + child.timeout = expect_timeout + return child + class Event: def __init__(self, name, args, kwargs): self.name = name Added: py/trunk/py/test/plugin/pytest_recwarn.py ============================================================================== --- (empty file) +++ py/trunk/py/test/plugin/pytest_recwarn.py Thu May 7 16:22:01 2009 @@ -0,0 +1,95 @@ +""" + +"recwarn" funcarg plugin that helps to assert +that warnings are shown to a user. See the test +at the bottom for an example. + +""" +import py +import os + +class RecwarnPlugin: + def pytest_funcarg__recwarn(self, request): + """ check that warnings have been raised. """ + warnings = WarningsRecorder() + request.addfinalizer(warnings.finalize) + return warnings + +class RecordedWarning: + def __init__(self, message, category, filename, lineno, line): + self.message = message + self.category = category + self.filename = filename + self.lineno = lineno + self.line = line + +class WarningsRecorder: + def __init__(self): + warningmodule = py.std.warnings + self.list = [] + def showwarning(message, category, filename, lineno, line=0): + self.list.append(RecordedWarning( + message, category, filename, lineno, line)) + try: + self.old_showwarning(message, category, + filename, lineno, line=line) + except TypeError: + # < python2.6 + self.old_showwarning(message, category, filename, lineno) + self.old_showwarning = warningmodule.showwarning + warningmodule.showwarning = showwarning + + def pop(self, cls=Warning): + """ pop the first recorded warning, raise exception if not exists.""" + for i, w in py.builtin.enumerate(self.list): + if issubclass(w.category, cls): + return self.list.pop(i) + __tracebackhide__ = True + assert 0, "%r not found in %r" %(cls, self.list) + + #def resetregistry(self): + # import warnings + # warnings.onceregistry.clear() + # warnings.__warningregistry__.clear() + + def clear(self): + self.list[:] = [] + + def finalize(self): + py.std.warnings.showwarning = self.old_showwarning + +def test_WarningRecorder(): + showwarning = py.std.warnings.showwarning + rec = WarningsRecorder() + assert py.std.warnings.showwarning != showwarning + assert not rec.list + py.std.warnings.warn_explicit("hello", UserWarning, "xyz", 13) + assert len(rec.list) == 1 + py.std.warnings.warn(DeprecationWarning("hello")) + assert len(rec.list) == 2 + warn = rec.pop() + assert str(warn.message) == "hello" + l = rec.list + rec.clear() + assert len(rec.list) == 0 + assert l is rec.list + py.test.raises(AssertionError, "rec.pop()") + rec.finalize() + assert showwarning == py.std.warnings.showwarning + +def test_recwarn_functional(testdir): + sorter = testdir.inline_runsource(""" + pytest_plugins = 'pytest_recwarn', + import warnings + oldwarn = warnings.showwarning + def test_method(recwarn): + assert warnings.showwarning != oldwarn + warnings.warn("hello") + warn = recwarn.pop() + assert isinstance(warn.message, UserWarning) + def test_finalized(): + assert warnings.showwarning == oldwarn + """) + res = sorter.countoutcomes() + assert tuple(res) == (2, 0, 0), res + Modified: py/trunk/py/test/plugin/pytest_restdoc.py ============================================================================== --- py/trunk/py/test/plugin/pytest_restdoc.py (original) +++ py/trunk/py/test/plugin/pytest_restdoc.py Thu May 7 16:22:01 2009 @@ -64,6 +64,9 @@ super(ReSTSyntaxTest, self).__init__(*args, **kwargs) self.project = project + def metainfo(self): + return self.fspath, None, "syntax check" + def runtest(self): self.restcheck(py.path.svnwc(self.fspath)) @@ -193,6 +196,9 @@ #return [] # no need to rebuild class DoctestText(py.test.collect.Item): + def metainfo(self): + return self.fspath, None, "doctest" + def runtest(self): content = self._normalize_linesep() newcontent = self.config.api.pytest_doctest_prepare_content(content=content) @@ -276,9 +282,9 @@ args=(tryfn, path, lineno), callobj=localrefcheck) class CheckLink(py.test.collect.Function): - def repr_metainfo(self): - return self.ReprMetaInfo(fspath=self.fspath, lineno=self._args[2], - modpath="checklink: %s" % (self._args[0],)) + def metainfo(self, basedir=None): + return (self.fspath, self._args[2], "checklink: %s" % self._args[0]) + def setup(self): pass def teardown(self): Modified: py/trunk/py/test/plugin/pytest_resultdb.py ============================================================================== --- py/trunk/py/test/plugin/pytest_resultdb.py (original) +++ py/trunk/py/test/plugin/pytest_resultdb.py Thu May 7 16:22:01 2009 @@ -6,6 +6,8 @@ Saves test results to a datastore. + XXX this needs to be merged with resultlog plugin + Also mixes in some early ideas about an archive abstraction for test results. """ @@ -262,7 +264,11 @@ class TestSQLiteResultArchive(BaseResultArchiveTests): cls = SQLiteResultArchive + def setup_method(self, method): + py.test.importorskip("sqlite3") + def test_init_db_sql(self, testdir): + py.test.importorskip("sqlite3") tempdb_path = unicode(testdir.tmpdir.join(self.tempdb)) archive = self.cls(tempdb_path) archive.init_db() @@ -287,6 +293,7 @@ class TestWithFunctionIntegration: def getarchive(self, testdir, arg): + py.test.importorskip("sqlite3") py.test.importorskip("simplejson") resultdb = testdir.tmpdir.join("resultdb") args = ["--resultdb=%s" % resultdb, "--resultdb_format=sqlite"] + [arg] Modified: py/trunk/py/test/plugin/pytest_terminal.py ============================================================================== --- py/trunk/py/test/plugin/pytest_terminal.py (original) +++ py/trunk/py/test/plugin/pytest_terminal.py Thu May 7 16:22:01 2009 @@ -138,8 +138,7 @@ def pytest_itemstart(self, item, node=None): if self.config.option.debug: - info = item.repr_metainfo() - line = info.verboseline(basedir=self.curdir) + " " + line = self._metainfoline(item) extra = "" if node: extra = "-> " + str(node.gateway.id) @@ -149,10 +148,11 @@ elif self.config.option.verbose and self.config.option.dist == "no": # ensure that the path is printed before the 1st test of # a module starts running - info = item.repr_metainfo() - line = info.verboseline(basedir=self.curdir) + " " - #self.write_fspath_result(fspath, "") + line = self._metainfoline(item) self.write_ensure_prefix(line, "") + else: + fspath, lineno, msg = item.metainfo() + self.write_fspath_result(fspath, "") def pytest_rescheduleitems(self, items): if self.config.option.debug: @@ -172,8 +172,7 @@ if not self.config.option.verbose: self.write_fspath_result(fspath, letter) else: - info = rep.colitem.repr_metainfo() - line = info.verboseline(basedir=self.curdir) + " " + line = self._metainfoline(rep.colitem) if not hasattr(rep, 'node'): self.write_ensure_prefix(line, word, **markup) else: @@ -250,6 +249,22 @@ for rootdir in rootdirs: self.write_line("### Watching: %s" %(rootdir,), bold=True) + def _metainfoline(self, item): + fspath, lineno, msg = item.metainfo() + if fspath: + fspath = self.curdir.bestrelpath(fspath) + if lineno is not None: + lineno += 1 + if fspath and lineno and msg: + line = "%(fspath)s:%(lineno)s: %(msg)s" + elif fspath and msg: + line = "%(fspath)s: %(msg)s" + elif fspath and lineno: + line = "%(fspath)s:%(lineno)s" + else: + line = "[nometainfo]" + return line % locals() + " " + # # summaries for testrunfinish # @@ -543,16 +558,10 @@ linecomp.stringio.truncate(0) def test_show_path_before_running_test(self, testdir, linecomp): - modcol = testdir.getmodulecol(""" - def test_foobar(): - pass - """) - rep = TerminalReporter(modcol.config, file=linecomp.stringio) - modcol.config.pluginmanager.register(rep) - l = list(testdir.genitems([modcol])) - assert len(l) == 1 - modcol.config.option.debug = True - rep.config.api.pytest_itemstart(item=l[0]) + item = testdir.getitem("def test_func(): pass") + rep = TerminalReporter(item.config, file=linecomp.stringio) + item.config.pluginmanager.register(rep) + rep.config.api.pytest_itemstart(item=item) linecomp.assert_contains_lines([ "*test_show_path_before_running_test.py*" ]) Modified: py/trunk/py/test/pycollect.py ============================================================================== --- py/trunk/py/test/pycollect.py (original) +++ py/trunk/py/test/pycollect.py Thu May 7 16:22:01 2009 @@ -87,15 +87,10 @@ self._fslineno = fspath, lineno return fspath, lineno - def repr_metainfo(self): + def metainfo(self): fspath, lineno = self.getfslineno() modpath = self.getmodpath() - return self.ReprMetaInfo( - fspath=fspath, - lineno=lineno, - modpath=modpath, - ) - + return fspath, lineno, modpath class PyCollectorMixin(PyobjMixin, py.test.collect.Collector): Class = configproperty('Class') @@ -235,6 +230,7 @@ class FunctionMixin(PyobjMixin): """ mixin for the code common to Function and Generator. """ + def _getsortvalue(self): return self.getfslineno() @@ -251,7 +247,7 @@ obj = self.parent.obj setup_func_or_method = getattr(obj, name, None) if setup_func_or_method is not None: - return setup_func_or_method(self.obj) + setup_func_or_method(self.obj) def teardown(self): """ perform teardown for this test function. """ @@ -283,9 +279,9 @@ class Generator(FunctionMixin, PyCollectorMixin, py.test.collect.Collector): def collect(self): - # test generators are collectors yet participate in - # the test-item setup and teardown protocol. - # otherwise we could avoid global setupstate + # test generators are seen as collectors but they also + # invoke setup/teardown on popular request + # (induced by the common "test_*" naming shared with normal tests) self.config._setupstate.prepare(self) l = [] seen = {} @@ -314,6 +310,7 @@ name = None call, args = obj[0], obj[1:] return name, call, args + # # Test Items @@ -348,14 +345,18 @@ def runtest(self): """ execute the given test function. """ - if not self._deprecated_testexecution(): - self.setupargs() # XXX move to setup() / consider funcargs plugin - ret = self.config.api.pytest_pyfunc_call( - pyfuncitem=self, args=self._args, kwargs=self.funcargs) + self.config.api.pytest_pyfunc_call(pyfuncitem=self, + args=self._args, kwargs=self.funcargs) - def setupargs(self): + def setup(self): + super(Function, self).setup() + self._setupfuncargs() + + def _setupfuncargs(self): if self._args: - # generator case: we don't do anything then + # functions yielded from a generator: we don't want + # to support that because we want to go here anyway: + # http://bitbucket.org/hpk42/py-trunk/issue/2/next-generation-generative-tests pass else: # standard Python Test function/method case @@ -389,7 +390,7 @@ def getrequest(self, argname): return FuncargRequest(pyfuncitem=self, argname=argname) - + class FuncargRequest: _argprefix = "pytest_funcarg__" @@ -447,8 +448,9 @@ name = name[len(self._argprefix):] if name not in available: available.append(name) - metainfo = self._pyfuncitem.repr_metainfo() - msg = "funcargument %r not found for: %s" %(self.argname,metainfo.verboseline()) + fspath, lineno, msg = self._pyfuncitem.metainfo() + line = "%s:%s" %(fspath, lineno) + msg = "funcargument %r not found for: %s" %(self.argname, line) msg += "\n available funcargs: %s" %(", ".join(available),) raise LookupError(msg) Modified: py/trunk/py/test/runner.py ============================================================================== --- py/trunk/py/test/runner.py (original) +++ py/trunk/py/test/runner.py Thu May 7 16:22:01 2009 @@ -20,7 +20,8 @@ item.config._setupstate.prepare(item) try: when = "execute" - res = item.runtest() + if not item._deprecated_testexecution(): + item.runtest() finally: when = "teardown" item.config._setupstate.teardown_exact(item) Modified: py/trunk/py/test/testing/acceptance_test.py ============================================================================== --- py/trunk/py/test/testing/acceptance_test.py (original) +++ py/trunk/py/test/testing/acceptance_test.py Thu May 7 16:22:01 2009 @@ -1,7 +1,5 @@ import py -pydir = py.path.local(py.__file__).dirpath() -pytestpath = pydir.join("bin", "py.test") EXPECTTIMEOUT=10.0 class TestGeneralUsage: @@ -445,30 +443,13 @@ class TestInteractive: - def getspawn(self, tmpdir): - pexpect = py.test.importorskip("pexpect") - basetemp = tmpdir.mkdir("basetemp") - def spawn(cmd): - cmd = cmd + " --basetemp=" + str(basetemp) - return pexpect.spawn(cmd, logfile=tmpdir.join("spawn.out").open("w")) - return spawn - - def requirespexpect(self, version_needed): - pexpect = py.test.importorskip("pexpect") - ver = tuple(map(int, pexpect.__version__.split("."))) - if ver < version_needed: - py.test.skip("pexpect version %s needed" %(".".join(map(str, version_needed)))) - def test_pdb_interaction(self, testdir): - self.requirespexpect((2,3)) - spawn = self.getspawn(testdir.tmpdir) p1 = testdir.makepyfile(""" def test_1(): i = 0 assert i == 1 """) - child = spawn("%s %s --pdb %s" % (py.std.sys.executable, pytestpath, p1)) - child.timeout = EXPECTTIMEOUT + child = testdir.spawn_pytest("--pdb %s" % p1) #child.expect(".*def test_1.*") child.expect(".*i = 0.*") child.expect("(Pdb)") @@ -478,14 +459,12 @@ child.wait() def test_simple_looponfail_interaction(self, testdir): - spawn = self.getspawn(testdir.tmpdir) p1 = testdir.makepyfile(""" def test_1(): assert 1 == 0 """) p1.setmtime(p1.mtime() - 50.0) - child = spawn("%s %s --looponfail %s" % (py.std.sys.executable, pytestpath, p1)) - child.timeout = EXPECTTIMEOUT + child = testdir.spawn_pytest("--looponfail %s" % p1) child.expect("assert 1 == 0") child.expect("test_simple_looponfail_interaction.py:") child.expect("1 failed") 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 Thu May 7 16:22:01 2009 @@ -131,12 +131,6 @@ names = [x.name for x in col.collect()] assert names == ["dir1", "dir2", "test_one.py", "test_two.py", "x"] - def test_collector_deprecated_run_method(self, testdir): - modcol = testdir.getmodulecol("pass") - res1 = py.test.deprecated_call(modcol.run) - res2 = modcol.collect() - assert res1 == [x.name for x in res2] - class TestCollectPluginHooks: def test_pytest_collect_file(self, testdir): tmpdir = testdir.tmpdir @@ -212,63 +206,3 @@ evrec = testdir.inline_run(testdir.tmpdir, "--XX") names = [rep.colitem.name for rep in evrec.getreports("collectreport")] assert 'hello' in names - -class TestCollectorReprs: - def test_repr_metainfo_basic_item(self, testdir): - modcol = testdir.getmodulecol("") - Item = py.test.collect.Item - item = Item("virtual", parent=modcol) - info = item.repr_metainfo() - assert info.fspath == modcol.fspath - assert not info.lineno - assert info.modpath == "Item" - - def test_repr_metainfo_func(self, testdir): - item = testdir.getitem("def test_func(): pass") - info = item.repr_metainfo() - assert info.fspath == item.fspath - assert info.lineno == 0 - assert info.modpath == "test_func" - - def test_repr_metainfo_class(self, testdir): - modcol = testdir.getmodulecol(""" - # lineno 0 - class TestClass: - def test_hello(self): pass - """) - classcol = modcol.collect_by_name("TestClass") - info = classcol.repr_metainfo() - assert info.fspath == modcol.fspath - assert info.lineno == 1 - assert info.modpath == "TestClass" - - def test_repr_metainfo_generator(self, testdir): - modcol = testdir.getmodulecol(""" - # lineno 0 - def test_gen(): - def check(x): - assert x - yield check, 3 - """) - gencol = modcol.collect_by_name("test_gen") - info = gencol.repr_metainfo() - assert info.fspath == modcol.fspath - assert info.lineno == 1 - assert info.modpath == "test_gen" - - genitem = gencol.collect()[0] - info = genitem.repr_metainfo() - assert info.fspath == modcol.fspath - assert info.lineno == 2 - assert info.modpath == "test_gen[0]" - """ - def test_func(): - pass - def test_genfunc(): - def check(x): - pass - yield check, 3 - class TestClass: - def test_method(self): - pass - """ Modified: py/trunk/py/test/testing/test_deprecated_api.py ============================================================================== --- py/trunk/py/test/testing/test_deprecated_api.py (original) +++ py/trunk/py/test/testing/test_deprecated_api.py Thu May 7 16:22:01 2009 @@ -2,15 +2,8 @@ import py class TestCollectDeprecated: - def test_directory_run_join_warnings(self, testdir): - p = testdir.makepyfile(test_one="") - config = testdir.parseconfig(p) - dirnode = config.getfsnode(p.dirpath()) - py.test.deprecated_call(dirnode.run) - # XXX for directories we still have join() - #py.test.deprecated_call(dirnode.join, 'test_one') - def test_collect_with_deprecated_run_and_join(self, testdir): + def test_collect_with_deprecated_run_and_join(self, testdir, recwarn): testdir.makepyfile(conftest=""" import py @@ -52,23 +45,31 @@ """) config = testdir.parseconfig() dirnode = config.getfsnode(p.dirpath()) - colitems = py.test.deprecated_call(dirnode.collect) + colitems = dirnode.collect() + w = recwarn.pop(DeprecationWarning) + assert w.filename.find("conftest.py") != -1 + #recwarn.resetregistry() + #assert 0, (w.message, w.filename, w.lineno) assert len(colitems) == 1 modcol = colitems[0] assert modcol.name == "somefile.py" - colitems = py.test.deprecated_call(modcol.collect) + colitems = modcol.collect() + recwarn.pop(DeprecationWarning) assert len(colitems) == 2 assert colitems[0].name == 'check' assert colitems[1].name == 'Cls' clscol = colitems[1] - colitems = py.test.deprecated_call(clscol.collect) + + colitems = clscol.collect() + recwarn.pop(DeprecationWarning) assert len(colitems) == 1 icol = colitems[0] - colitems = py.test.deprecated_call(icol.collect) + colitems = icol.collect() + recwarn.pop(DeprecationWarning) assert len(colitems) == 1 assert colitems[0].name == 'check2' - def test_collect_with_deprecated_join_but_no_run(self, testdir): + def test_collect_with_deprecated_join_but_no_run(self, testdir, recwarn): testdir.makepyfile(conftest=""" import py @@ -87,42 +88,57 @@ def check_one(): pass class SomeClass: pass """) - colitems = py.test.deprecated_call(col.collect) + colitems = col.collect() + recwarn.pop(DeprecationWarning) assert len(colitems) == 1 funcitem = colitems[0] assert funcitem.name == "check_one" - def test_function_custom_run(self, testdir): + def test_function_custom_run(self, testdir, recwarn): testdir.makepyfile(conftest=""" import py - class MyFunction(py.test.collect.Function): + class Function(py.test.collect.Function): def run(self): pass - Function=MyFunction """) modcol = testdir.getmodulecol("def test_func(): pass") funcitem = modcol.collect()[0] assert funcitem.name == 'test_func' - py.test.deprecated_call(funcitem.runtest) + recwarn.clear() + funcitem._deprecated_testexecution() + recwarn.pop(DeprecationWarning) - def test_function_custom_execute(self, testdir): + def test_function_custom_execute(self, testdir, recwarn): testdir.makepyfile(conftest=""" import py + class MyFunction(py.test.collect.Function): def execute(self, obj, *args): pass Function=MyFunction """) - modcol = testdir.getmodulecol("def test_func(): pass") + modcol = testdir.getmodulecol("def test_func2(): pass") funcitem = modcol.collect()[0] - assert funcitem.name == 'test_func' - py.test.deprecated_call(funcitem.runtest) + assert funcitem.name == 'test_func2' + funcitem._deprecated_testexecution() + w = recwarn.pop(DeprecationWarning) + assert w.filename.find("conftest.py") != -1 - def test_function_deprecated_run_execute(self, testdir): - modcol = testdir.getmodulecol("def test_some(): pass") + def test_function_deprecated_run_execute(self, testdir, recwarn): + testdir.makepyfile(conftest=""" + import py + + class Function(py.test.collect.Function): + + def run(self): + pass + """) + modcol = testdir.getmodulecol("def test_some2(): pass") funcitem = modcol.collect()[0] - py.test.deprecated_call(funcitem.run) - py.test.deprecated_call(funcitem.execute, funcitem.obj) + + recwarn.clear() + funcitem._deprecated_testexecution() + recwarn.pop(DeprecationWarning) def test_function_deprecated_run_recursive(self, testdir): testdir.makepyfile(conftest=""" Modified: py/trunk/py/test/testing/test_funcargs.py ============================================================================== --- py/trunk/py/test/testing/test_funcargs.py (original) +++ py/trunk/py/test/testing/test_funcargs.py Thu May 7 16:22:01 2009 @@ -8,7 +8,7 @@ return 42 """) item = testdir.getitem("def test_func(some): pass") - exc = py.test.raises(LookupError, "item.setupargs()") + exc = py.test.raises(LookupError, "item._setupfuncargs()") s = str(exc.value) assert s.find("xyzsomething") != -1 @@ -18,7 +18,7 @@ def pytest_funcarg__some(self, request): return request.function.__name__ item.config.pluginmanager.register(Provider()) - item.setupargs() + item._setupfuncargs() assert len(item.funcargs) == 1 def test_funcarg_lookup_default_gets_overriden(self, testdir): @@ -27,7 +27,7 @@ def pytest_funcarg__other(self, request): return request.function.__name__ item.config.pluginmanager.register(Provider()) - item.setupargs() + item._setupfuncargs() assert len(item.funcargs) == 1 name, value = item.funcargs.popitem() assert name == "other" @@ -41,7 +41,7 @@ def pytest_funcarg__other(self, request): return 42 item.config.pluginmanager.register(Provider()) - item.setupargs() + item._setupfuncargs() assert len(item.funcargs) == 2 assert item.funcargs['some'] == "test_func" assert item.funcargs['other'] == 42 @@ -58,9 +58,9 @@ pass """) item1, item2 = testdir.genitems([modcol]) - item1.setupargs() + item1._setupfuncargs() assert item1.funcargs['something'] == "test_method" - item2.setupargs() + item2._setupfuncargs() assert item2.funcargs['something'] == "test_func" class TestRequest: Modified: py/trunk/py/test/testing/test_pycollect.py ============================================================================== --- py/trunk/py/test/testing/test_pycollect.py (original) +++ py/trunk/py/test/testing/test_pycollect.py Thu May 7 16:22:01 2009 @@ -339,3 +339,55 @@ assert len(colitems) == 1 assert colitems[0].name == "check_method" + +class TestMetaInfo: + + def test_func_metainfo(self, testdir): + item = testdir.getitem("def test_func(): pass") + fspath, lineno, modpath = item.metainfo() + assert fspath == item.fspath + assert lineno == 0 + assert modpath == "test_func" + + def test_class_metainfo(self, testdir): + modcol = testdir.getmodulecol(""" + # lineno 0 + class TestClass: + def test_hello(self): pass + """) + classcol = modcol.collect_by_name("TestClass") + fspath, lineno, msg = classcol.metainfo() + assert fspath == modcol.fspath + assert lineno == 1 + assert msg == "TestClass" + + def test_generator_metainfo(self, testdir): + modcol = testdir.getmodulecol(""" + # lineno 0 + def test_gen(): + def check(x): + assert x + yield check, 3 + """) + gencol = modcol.collect_by_name("test_gen") + fspath, lineno, modpath = gencol.metainfo() + assert fspath == modcol.fspath + assert lineno == 1 + assert modpath == "test_gen" + + genitem = gencol.collect()[0] + fspath, lineno, modpath = genitem.metainfo() + assert fspath == modcol.fspath + assert lineno == 2 + assert modpath == "test_gen[0]" + """ + def test_func(): + pass + def test_genfunc(): + def check(x): + pass + yield check, 3 + class TestClass: + def test_method(self): + pass + """ From hpk at codespeak.net Thu May 7 19:37:48 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Thu, 7 May 2009 19:37:48 +0200 (CEST) Subject: [py-svn] r65143 - in py/trunk/py: . execnet execnet/testing log misc misc/testing test test/dist test/looponfail test/plugin test/testing Message-ID: <20090507173748.6E08916855C@codespeak.net> Author: hpk Date: Thu May 7 19:37:45 2009 New Revision: 65143 Modified: py/trunk/py/__init__.py py/trunk/py/_com.py py/trunk/py/execnet/gateway.py py/trunk/py/execnet/gwmanage.py py/trunk/py/execnet/register.py py/trunk/py/execnet/testing/test_gwmanage.py py/trunk/py/log/consumer.py py/trunk/py/misc/std.py py/trunk/py/misc/testing/test_com.py py/trunk/py/misc/testing/test_std.py py/trunk/py/test/collect.py py/trunk/py/test/config.py py/trunk/py/test/dist/dsession.py py/trunk/py/test/dist/nodemanage.py py/trunk/py/test/looponfail/remote.py py/trunk/py/test/plugin/pytest__pytest.py py/trunk/py/test/plugin/pytest_default.py py/trunk/py/test/plugin/pytest_doctest.py py/trunk/py/test/plugin/pytest_eventlog.py py/trunk/py/test/plugin/pytest_execnetcleanup.py py/trunk/py/test/plugin/pytest_figleaf.py py/trunk/py/test/plugin/pytest_iocapture.py py/trunk/py/test/plugin/pytest_plugintester.py py/trunk/py/test/plugin/pytest_pocoo.py py/trunk/py/test/plugin/pytest_pylint.py py/trunk/py/test/plugin/pytest_pytester.py py/trunk/py/test/plugin/pytest_restdoc.py py/trunk/py/test/plugin/pytest_resultdb.py py/trunk/py/test/plugin/pytest_resultlog.py py/trunk/py/test/plugin/pytest_runner.py py/trunk/py/test/plugin/pytest_terminal.py py/trunk/py/test/plugin/pytest_tmpdir.py py/trunk/py/test/plugin/pytest_unittest.py py/trunk/py/test/plugin/pytest_xfail.py py/trunk/py/test/pluginmanager.py py/trunk/py/test/pycollect.py py/trunk/py/test/runner.py py/trunk/py/test/session.py py/trunk/py/test/testing/test_api.py py/trunk/py/test/testing/test_pluginmanager.py Log: merging from mercurial: * change api -> hook * fix issue 6 (py.std exception) Modified: py/trunk/py/__init__.py ============================================================================== --- py/trunk/py/__init__.py (original) +++ py/trunk/py/__init__.py Thu May 7 19:37:45 2009 @@ -54,10 +54,10 @@ exportdefs = { # py lib events and plugins - '_com.Registry' : ('./_com.py', 'Registry'), + '_com.Registry' : ('./_com.py', 'Registry'), '_com.MultiCall' : ('./_com.py', 'MultiCall'), - '_com.comregistry' : ('./_com.py', 'comregistry'), - '_com.PluginAPI' : ('./_com.py', 'PluginAPI'), + '_com.comregistry' : ('./_com.py', 'comregistry'), + '_com.Hooks' : ('./_com.py', 'Hooks'), # py lib cmdline tools 'cmdline.pytest' : ('./cmdline/pytest.py', 'main',), @@ -146,7 +146,7 @@ # gateways into remote contexts 'execnet.__doc__' : ('./execnet/__init__.py', '__doc__'), - 'execnet._API' : ('./execnet/gateway.py', 'ExecnetAPI'), + 'execnet._HookSpecs' : ('./execnet/gateway.py', 'ExecnetAPI'), 'execnet.SocketGateway' : ('./execnet/register.py', 'SocketGateway'), 'execnet.PopenGateway' : ('./execnet/register.py', 'PopenGateway'), 'execnet.SshGateway' : ('./execnet/register.py', 'SshGateway'), Modified: py/trunk/py/_com.py ============================================================================== --- py/trunk/py/_com.py (original) +++ py/trunk/py/_com.py Thu May 7 19:37:45 2009 @@ -108,21 +108,21 @@ *args, **kwargs).execute(firstresult=True) -class PluginAPI: - def __init__(self, apiclass, registry=None): - self._apiclass = apiclass +class Hooks: + def __init__(self, hookspecs, registry=None): + self._hookspecs = hookspecs if registry is None: registry = comregistry self.registry = registry - for name, method in vars(apiclass).items(): + for name, method in vars(hookspecs).items(): if name[:2] != "__": firstresult = getattr(method, 'firstresult', False) - mm = ApiCall(registry, name, firstresult=firstresult) + mm = HookCall(registry, name, firstresult=firstresult) setattr(self, name, mm) def __repr__(self): - return "" %(self._apiclass, self._plugins) + return "" %(self._hookspecs, self._plugins) -class ApiCall: +class HookCall: def __init__(self, registry, name, firstresult): self.registry = registry self.name = name @@ -130,7 +130,7 @@ def __repr__(self): mode = self.firstresult and "firstresult" or "each" - return "" %(self.name, mode, self.registry) + return "" %(self.name, mode, self.registry) def __call__(self, *args, **kwargs): if args: Modified: py/trunk/py/execnet/gateway.py ============================================================================== --- py/trunk/py/execnet/gateway.py (original) +++ py/trunk/py/execnet/gateway.py Thu May 7 19:37:45 2009 @@ -88,10 +88,10 @@ self._channelfactory = ChannelFactory(self, _startcount) self._cleanup.register(self) if _startcount == 1: # only import 'py' on the "client" side - from py._com import PluginAPI - self.api = PluginAPI(ExecnetAPI) + from py._com import Hooks + self.hook = Hooks(ExecnetAPI) else: - self.api = ExecnetAPI() + self.hook = ExecnetAPI() def _initreceive(self, requestqueue=False): if requestqueue: @@ -353,7 +353,7 @@ self._cleanup.unregister(self) self._stopexec() self._stopsend() - self.api.pyexecnet_gateway_exit(gateway=self) + self.hook.pyexecnet_gateway_exit(gateway=self) def _remote_redirect(self, stdout=None, stderr=None): """ return a handle representing a redirection of a remote Modified: py/trunk/py/execnet/gwmanage.py ============================================================================== --- py/trunk/py/execnet/gwmanage.py (original) +++ py/trunk/py/execnet/gwmanage.py Thu May 7 19:37:45 2009 @@ -21,7 +21,7 @@ if not spec.chdir and not spec.popen: spec.chdir = defaultchdir self.specs.append(spec) - self.api = py._com.PluginAPI(py.execnet._API) + self.hook = py._com.Hooks(py.execnet._HookSpecs) def makegateways(self): assert not self.gateways @@ -29,7 +29,7 @@ gw = py.execnet.makegateway(spec) self.gateways.append(gw) gw.id = "[%s]" % len(self.gateways) - self.api.pyexecnet_gwmanage_newgateway( + self.hook.pyexecnet_gwmanage_newgateway( gateway=gw, platinfo=gw._rinfo()) def getgateways(self, remote=True, inplacelocal=True): @@ -75,12 +75,12 @@ rsync.add_target_host(gateway, finished=finished) seen[spec] = gateway if seen: - self.api.pyexecnet_gwmanage_rsyncstart( + self.hook.pyexecnet_gwmanage_rsyncstart( source=source, gateways=seen.values(), ) rsync.send() - self.api.pyexecnet_gwmanage_rsyncfinish( + self.hook.pyexecnet_gwmanage_rsyncfinish( source=source, gateways=seen.values() ) Modified: py/trunk/py/execnet/register.py ============================================================================== --- py/trunk/py/execnet/register.py (original) +++ py/trunk/py/execnet/register.py Thu May 7 19:37:45 2009 @@ -40,7 +40,7 @@ super(InstallableGateway, self).__init__(io=io, _startcount=1) # XXX we dissallow execution form the other side self._initreceive(requestqueue=False) - self.api.pyexecnet_gateway_init(gateway=self) + self.hook.pyexecnet_gateway_init(gateway=self) def _remote_bootstrap_gateway(self, io, extra=''): """ return Gateway with a asynchronously remotely Modified: py/trunk/py/execnet/testing/test_gwmanage.py ============================================================================== --- py/trunk/py/execnet/testing/test_gwmanage.py (original) +++ py/trunk/py/execnet/testing/test_gwmanage.py Thu May 7 19:37:45 2009 @@ -21,7 +21,7 @@ assert spec.chdir == "abc" def test_popen_makegateway_events(self, _pytest): - rec = _pytest.getcallrecorder(py.execnet._API) + rec = _pytest.getcallrecorder(py.execnet._HookSpecs) hm = GatewayManager(["popen"] * 2) hm.makegateways() call = rec.popcall("pyexecnet_gwmanage_newgateway") @@ -63,7 +63,7 @@ def test_hostmanage_rsync_same_popen_twice(self, mysetup, _pytest): source, dest = mysetup.source, mysetup.dest - rec = _pytest.getcallrecorder(py.execnet._API) + rec = _pytest.getcallrecorder(py.execnet._HookSpecs) hm = GatewayManager(["popen//chdir=%s" %dest] * 2) hm.makegateways() source.ensure("dir1", "dir2", "hello") Modified: py/trunk/py/log/consumer.py ============================================================================== --- py/trunk/py/log/consumer.py (original) +++ py/trunk/py/log/consumer.py Thu May 7 19:37:45 2009 @@ -49,7 +49,7 @@ 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)) - except ImportError: + except AttributeError: pass def __init__(self, priority = None): Modified: py/trunk/py/misc/std.py ============================================================================== --- py/trunk/py/misc/std.py (original) +++ py/trunk/py/misc/std.py Thu May 7 19:37:45 2009 @@ -2,11 +2,18 @@ import sys class Std(object): - """ lazily import standard modules """ + """ makes all standard python modules available as a lazily + computed attribute. + """ + def __init__(self): self.__dict__ = sys.modules def __getattr__(self, name): - return __import__(name) + try: + m = __import__(name) + except ImportError: + raise AttributeError("py.std: could not import %s" % name) + return m std = Std() Modified: py/trunk/py/misc/testing/test_com.py ============================================================================== --- py/trunk/py/misc/testing/test_com.py (original) +++ py/trunk/py/misc/testing/test_com.py Thu May 7 19:37:45 2009 @@ -2,7 +2,7 @@ import py import os from py._com import Registry, MultiCall -from py._com import PluginAPI +from py._com import Hooks pytest_plugins = "xfail" @@ -144,14 +144,14 @@ def test_api_and_defaults(): assert isinstance(py._com.comregistry, Registry) -class TestPluginAPI: +class TestHooks: def test_happypath(self): registry = Registry() class Api: def hello(self, arg): pass - mcm = PluginAPI(apiclass=Api, registry=registry) + mcm = Hooks(hookspecs=Api, registry=registry) assert hasattr(mcm, 'hello') assert repr(mcm.hello).find("hello") != -1 class Plugin: @@ -167,7 +167,7 @@ class Api: def hello(self, arg): pass - mcm = PluginAPI(apiclass=Api, registry=registry) + mcm = Hooks(hookspecs=Api, registry=registry) excinfo = py.test.raises(TypeError, "mcm.hello(3)") assert str(excinfo.value).find("only keyword arguments") != -1 assert str(excinfo.value).find("hello(self, arg)") @@ -178,7 +178,7 @@ def hello(self, arg): pass hello.firstresult = True - mcm = PluginAPI(apiclass=Api, registry=registry) + mcm = Hooks(hookspecs=Api, registry=registry) class Plugin: def hello(self, arg): return arg + 1 @@ -188,5 +188,5 @@ def test_default_plugins(self): class Api: pass - mcm = PluginAPI(apiclass=Api) + mcm = Hooks(hookspecs=Api) assert mcm.registry == py._com.comregistry Modified: py/trunk/py/misc/testing/test_std.py ============================================================================== --- py/trunk/py/misc/testing/test_std.py (original) +++ py/trunk/py/misc/testing/test_std.py Thu May 7 19:37:45 2009 @@ -6,7 +6,7 @@ assert py.std.os is os def test_import_error_converts_to_attributeerror(): - py.test.raises(ImportError, "py.std.xyzalskdj") + py.test.raises(AttributeError, "py.std.xyzalskdj") def test_std_gets_it(): for x in py.std.sys.modules: Modified: py/trunk/py/test/collect.py ============================================================================== --- py/trunk/py/test/collect.py (original) +++ py/trunk/py/test/collect.py Thu May 7 19:37:45 2009 @@ -409,14 +409,14 @@ return res def consider_file(self, path): - return self.config.api.pytest_collect_file(path=path, parent=self) + return self.config.hook.pytest_collect_file(path=path, parent=self) def consider_dir(self, path, usefilters=None): if usefilters is not None: py.log._apiwarn("0.99", "usefilters argument not needed") - res = self.config.api.pytest_collect_recurse(path=path, parent=self) + res = self.config.hook.pytest_collect_recurse(path=path, parent=self) if res is None or res: - return self.config.api.pytest_collect_directory( + return self.config.hook.pytest_collect_directory( path=path, parent=self) class Item(Node): Modified: py/trunk/py/test/config.py ============================================================================== --- py/trunk/py/test/config.py (original) +++ py/trunk/py/test/config.py Thu May 7 19:37:45 2009 @@ -42,7 +42,7 @@ self.pluginmanager = pluginmanager self._conftest = Conftest(onimport=self._onimportconftest) self._setupstate = SetupState() - self.api = pluginmanager.api + self.hook = pluginmanager.hook def _onimportconftest(self, conftestmodule): self.trace("loaded conftestmodule %r" %(conftestmodule,)) @@ -50,7 +50,7 @@ def trace(self, msg): if getattr(self.option, 'traceconfig', None): - self.api.pytest_trace(category="config", msg=msg) + self.hook.pytest_trace(category="config", msg=msg) def _processopt(self, opt): if hasattr(opt, 'default') and opt.dest: Modified: py/trunk/py/test/dist/dsession.py ============================================================================== --- py/trunk/py/test/dist/dsession.py (original) +++ py/trunk/py/test/dist/dsession.py Thu May 7 19:37:45 2009 @@ -97,7 +97,7 @@ callname, args, kwargs = eventcall if callname is not None: - call = getattr(self.config.api, callname) + call = getattr(self.config.hook, callname) assert not args call(**kwargs) @@ -114,7 +114,7 @@ # events other than HostDown upstream eventname, args, kwargs = self.queue.get() if eventname == "pytest_testnodedown": - self.config.api.pytest_testnodedown(**kwargs) + self.config.hook.pytest_testnodedown(**kwargs) self.removenode(kwargs['node']) if not self.node2pending: # finished @@ -176,7 +176,7 @@ if isinstance(next, py.test.collect.Item): senditems.append(next) else: - self.config.api.pytest_collectstart(collector=next) + self.config.hook.pytest_collectstart(collector=next) self.queueevent("pytest_collectreport", rep=basic_collect_report(next)) if self.config.option.dist == "each": self.senditems_each(senditems) @@ -199,7 +199,7 @@ pending.extend(sending) for item in sending: self.item2nodes.setdefault(item, []).append(node) - self.config.api.pytest_itemstart(item=item, node=node) + self.config.hook.pytest_itemstart(item=item, node=node) tosend[:] = tosend[room:] # update inplace if tosend: # we have some left, give it to the main loop @@ -218,7 +218,7 @@ # "sending same item %r to multiple " # "not implemented" %(item,)) self.item2nodes.setdefault(item, []).append(node) - self.config.api.pytest_itemstart(item=item, node=node) + self.config.hook.pytest_itemstart(item=item, node=node) pending.extend(sending) tosend[:] = tosend[room:] # update inplace if not tosend: @@ -241,7 +241,7 @@ longrepr = "!!! Node %r crashed during running of test %r" %(node, item) rep = ItemTestReport(item, when="???", excinfo=longrepr) rep.node = node - self.config.api.pytest_itemtestreport(rep=rep) + self.config.hook.pytest_itemtestreport(rep=rep) def setup(self): """ setup any neccessary resources ahead of the test run. """ Modified: py/trunk/py/test/dist/nodemanage.py ============================================================================== --- py/trunk/py/test/dist/nodemanage.py (original) +++ py/trunk/py/test/dist/nodemanage.py Thu May 7 19:37:45 2009 @@ -15,7 +15,7 @@ self._nodesready = py.std.threading.Event() def trace(self, msg): - self.config.api.pytest_trace(category="nodemanage", msg=msg) + self.config.hook.pytest_trace(category="nodemanage", msg=msg) def config_getignores(self): return self.config.getconftest_pathlist("rsyncignore") Modified: py/trunk/py/test/looponfail/remote.py ============================================================================== --- py/trunk/py/test/looponfail/remote.py (original) +++ py/trunk/py/test/looponfail/remote.py Thu May 7 19:37:45 2009 @@ -147,7 +147,7 @@ DEBUG("SLAVE: starting session.main()") session.main(colitems) - session.config.api.pytest_looponfailinfo( + session.config.hook.pytest_looponfailinfo( failreports=list(failreports), rootdirs=[config.topdir]) channel.send([x.colitem._totrail() for x in failreports]) Modified: py/trunk/py/test/plugin/pytest__pytest.py ============================================================================== --- py/trunk/py/test/plugin/pytest__pytest.py (original) +++ py/trunk/py/test/plugin/pytest__pytest.py Thu May 7 19:37:45 2009 @@ -8,11 +8,11 @@ def __init__(self, request): self.request = request - def getcallrecorder(self, apiclass, comregistry=None): + def getcallrecorder(self, hookspecs, comregistry=None): if comregistry is None: comregistry = self.request.config.pluginmanager.comregistry callrecorder = CallRecorder(comregistry) - callrecorder.start_recording(apiclass) + callrecorder.start_recording(hookspecs) self.request.addfinalizer(callrecorder.finalize) return callrecorder @@ -35,17 +35,17 @@ self.calls = [] self._recorders = {} - def start_recording(self, apiclass): - assert apiclass not in self._recorders + def start_recording(self, hookspecs): + assert hookspecs not in self._recorders class RecordCalls: _recorder = self - for name, method in vars(apiclass).items(): + for name, method in vars(hookspecs).items(): if name[0] != "_": setattr(RecordCalls, name, self._getcallparser(method)) recorder = RecordCalls() - self._recorders[apiclass] = recorder + self._recorders[hookspecs] = recorder self._comregistry.register(recorder) - self.api = py._com.PluginAPI(apiclass, registry=self._comregistry) + self.hook = py._com.Hooks(hookspecs, registry=self._comregistry) def finalize(self): for recorder in self._recorders.values(): @@ -53,8 +53,8 @@ self._recorders.clear() def recordsmethod(self, name): - for apiclass in self._recorders: - if hasattr(apiclass, name): + for hookspecs in self._recorders: + if hasattr(hookspecs, name): return True def _getcallparser(self, method): @@ -97,7 +97,7 @@ return l def test_generic(plugintester): - plugintester.apicheck(_pytestPlugin) + plugintester.hookcheck(_pytestPlugin) def test_callrecorder_basic(): comregistry = py._com.Registry() @@ -106,7 +106,7 @@ def xyz(self, arg): pass rec.start_recording(ApiClass) - rec.api.xyz(arg=123) + rec.hook.xyz(arg=123) call = rec.popcall("xyz") assert call.arg == 123 assert call._name == "xyz" @@ -124,7 +124,7 @@ def xyz(self, arg): return arg + 1 rec._comregistry.register(Plugin()) - res = rec.api.xyz(arg=41) + res = rec.hook.xyz(arg=41) assert res == [42] """) sorter.assertoutcome(passed=1) Modified: py/trunk/py/test/plugin/pytest_default.py ============================================================================== --- py/trunk/py/test/plugin/pytest_default.py (original) +++ py/trunk/py/test/plugin/pytest_default.py Thu May 7 19:37:45 2009 @@ -10,7 +10,7 @@ else: runner = basic_run_report report = runner(item, pdb=pdb) - item.config.api.pytest_itemtestreport(rep=report) + item.config.hook.pytest_itemtestreport(rep=report) return True def pytest_item_makereport(self, item, excinfo, when, outerr): @@ -20,7 +20,7 @@ def pytest_item_runtest_finished(self, item, excinfo, outerr): from py.__.test import runner rep = runner.ItemTestReport(item, excinfo, "execute", outerr) - item.config.api.pytest_itemtestreport(rep=rep) + item.config.hook.pytest_itemtestreport(rep=rep) def pytest_pyfunc_call(self, pyfuncitem, args, kwargs): pyfuncitem.obj(*args, **kwargs) @@ -187,7 +187,7 @@ assert x('-f') == 'LooponfailingSession' def test_generic(plugintester): - plugintester.apicheck(DefaultPlugin) + plugintester.hookcheck(DefaultPlugin) def test_plugin_specify(testdir): testdir.chdir() Modified: py/trunk/py/test/plugin/pytest_doctest.py ============================================================================== --- py/trunk/py/test/plugin/pytest_doctest.py (original) +++ py/trunk/py/test/plugin/pytest_doctest.py Thu May 7 19:37:45 2009 @@ -162,5 +162,5 @@ def test_generic(plugintester): - plugintester.apicheck(DoctestPlugin) + plugintester.hookcheck(DoctestPlugin) Modified: py/trunk/py/test/plugin/pytest_eventlog.py ============================================================================== --- py/trunk/py/test/plugin/pytest_eventlog.py (original) +++ py/trunk/py/test/plugin/pytest_eventlog.py Thu May 7 19:37:45 2009 @@ -28,7 +28,7 @@ @py.test.mark.xfail def test_generic(plugintester): - plugintester.apicheck(EventlogPlugin) + plugintester.hookcheck(EventlogPlugin) testdir = plugintester.testdir() testdir.makepyfile(""" Modified: py/trunk/py/test/plugin/pytest_execnetcleanup.py ============================================================================== --- py/trunk/py/test/plugin/pytest_execnetcleanup.py (original) +++ py/trunk/py/test/plugin/pytest_execnetcleanup.py Thu May 7 19:37:45 2009 @@ -43,7 +43,7 @@ return res def test_generic(plugintester): - plugintester.apicheck(ExecnetcleanupPlugin) + plugintester.hookcheck(ExecnetcleanupPlugin) @py.test.mark.xfail("clarify plugin registration/unregistration") def test_execnetplugin(testdir): Modified: py/trunk/py/test/plugin/pytest_figleaf.py ============================================================================== --- py/trunk/py/test/plugin/pytest_figleaf.py (original) +++ py/trunk/py/test/plugin/pytest_figleaf.py Thu May 7 19:37:45 2009 @@ -55,7 +55,7 @@ def test_generic(plugintester): - plugintester.apicheck(FigleafPlugin) + plugintester.hookcheck(FigleafPlugin) def test_functional(testdir): py.test.importorskip("figleaf") Modified: py/trunk/py/test/plugin/pytest_iocapture.py ============================================================================== --- py/trunk/py/test/plugin/pytest_iocapture.py (original) +++ py/trunk/py/test/plugin/pytest_iocapture.py Thu May 7 19:37:45 2009 @@ -26,7 +26,7 @@ return res def test_generic(plugintester): - plugintester.apicheck(IocapturePlugin) + plugintester.hookcheck(IocapturePlugin) class TestCapture: def test_std_functional(self, testdir): Modified: py/trunk/py/test/plugin/pytest_plugintester.py ============================================================================== --- py/trunk/py/test/plugin/pytest_plugintester.py (original) +++ py/trunk/py/test/plugin/pytest_plugintester.py Thu May 7 19:37:45 2009 @@ -25,7 +25,7 @@ # break return crunner - def apicheck(self, pluginclass): + def hookcheck(self, pluginclass): print "loading and checking", pluginclass fail = False pm = py.test._PluginManager() @@ -84,4 +84,4 @@ # =============================================================================== def test_generic(plugintester): - plugintester.apicheck(PlugintesterPlugin) + plugintester.hookcheck(PlugintesterPlugin) Modified: py/trunk/py/test/plugin/pytest_pocoo.py ============================================================================== --- py/trunk/py/test/plugin/pytest_pocoo.py (original) +++ py/trunk/py/test/plugin/pytest_pocoo.py Thu May 7 19:37:45 2009 @@ -41,7 +41,7 @@ def test_apicheck(plugintester): - plugintester.apicheck(PocooPlugin) + plugintester.hookcheck(PocooPlugin) def test_toproxy(testdir, monkeypatch): l = [] Modified: py/trunk/py/test/plugin/pytest_pylint.py ============================================================================== --- py/trunk/py/test/plugin/pytest_pylint.py (original) +++ py/trunk/py/test/plugin/pytest_pylint.py Thu May 7 19:37:45 2009 @@ -51,7 +51,7 @@ print rating def test_generic(plugintester): - plugintester.apicheck(PylintPlugin) + plugintester.hookcheck(PylintPlugin) #def test_functional Modified: py/trunk/py/test/plugin/pytest_pytester.py ============================================================================== --- py/trunk/py/test/plugin/pytest_pytester.py (original) +++ py/trunk/py/test/plugin/pytest_pytester.py Thu May 7 19:37:45 2009 @@ -28,7 +28,7 @@ return evrec def test_generic(plugintester): - plugintester.apicheck(PytesterPlugin) + plugintester.hookcheck(PytesterPlugin) class RunResult: def __init__(self, ret, outlines, errlines): @@ -78,7 +78,7 @@ sorter = EventRecorder(registry) sorter.callrecorder = CallRecorder(registry) sorter.callrecorder.start_recording(api.PluginHooks) - sorter.api = sorter.callrecorder.api + sorter.hook = sorter.callrecorder.hook self.request.addfinalizer(sorter.callrecorder.finalize) return sorter @@ -385,7 +385,7 @@ rep = runner.ItemTestReport(None, None) rep.passed = False rep.failed = True - recorder.api.pytest_itemtestreport(rep=rep) + recorder.hook.pytest_itemtestreport(rep=rep) failures = recorder.getfailures() assert failures == [rep] failures = recorder.getfailures() @@ -394,12 +394,12 @@ rep = runner.ItemTestReport(None, None) rep.passed = False rep.skipped = True - recorder.api.pytest_itemtestreport(rep=rep) + recorder.hook.pytest_itemtestreport(rep=rep) rep = runner.CollectReport(None, None) rep.passed = False rep.failed = True - recorder.api.pytest_itemtestreport(rep=rep) + recorder.hook.pytest_itemtestreport(rep=rep) passed, skipped, failed = recorder.listoutcomes() assert not passed and skipped and failed @@ -412,7 +412,7 @@ recorder.unregister() recorder.clear() assert not recorder.getfailures() - recorder.api.pytest_itemtestreport(rep=rep) + recorder.hook.pytest_itemtestreport(rep=rep) assert not recorder.getfailures() class LineComp: Modified: py/trunk/py/test/plugin/pytest_restdoc.py ============================================================================== --- py/trunk/py/test/plugin/pytest_restdoc.py (original) +++ py/trunk/py/test/plugin/pytest_restdoc.py Thu May 7 19:37:45 2009 @@ -142,7 +142,7 @@ directives.register_directive('sourcecode', pygments_directive) def resolve_linkrole(self, name, text, check=True): - apigen_relpath = self.project.apigen_relpath + apigen_relpath = self.project.hookgen_relpath if name == 'api': if text == 'py': @@ -201,7 +201,7 @@ def runtest(self): content = self._normalize_linesep() - newcontent = self.config.api.pytest_doctest_prepare_content(content=content) + newcontent = self.config.hook.pytest_doctest_prepare_content(content=content) if newcontent is not None: content = newcontent s = content @@ -346,7 +346,7 @@ # PLUGIN tests # def test_generic(plugintester): - plugintester.apicheck(RestdocPlugin) + plugintester.hookcheck(RestdocPlugin) def test_deindent(): assert deindent('foo') == 'foo' Modified: py/trunk/py/test/plugin/pytest_resultdb.py ============================================================================== --- py/trunk/py/test/plugin/pytest_resultdb.py (original) +++ py/trunk/py/test/plugin/pytest_resultdb.py Thu May 7 19:37:45 2009 @@ -365,7 +365,7 @@ assert 'ValueError' in entry def test_generic(plugintester): - plugintester.apicheck(ResultdbPlugin) + plugintester.hookcheck(ResultdbPlugin) testdir = plugintester.testdir() testdir.makepyfile(""" import py Modified: py/trunk/py/test/plugin/pytest_resultlog.py ============================================================================== --- py/trunk/py/test/plugin/pytest_resultlog.py (original) +++ py/trunk/py/test/plugin/pytest_resultlog.py Thu May 7 19:37:45 2009 @@ -224,7 +224,7 @@ assert 'ValueError' in entry def test_generic(plugintester, LineMatcher): - plugintester.apicheck(ResultlogPlugin) + plugintester.hookcheck(ResultlogPlugin) testdir = plugintester.testdir() testdir.plugins.append("resultlog") testdir.makepyfile(""" Modified: py/trunk/py/test/plugin/pytest_runner.py ============================================================================== --- py/trunk/py/test/plugin/pytest_runner.py (original) +++ py/trunk/py/test/plugin/pytest_runner.py Thu May 7 19:37:45 2009 @@ -13,15 +13,15 @@ call = item.config.guardedcall(lambda: setupstate.prepare(item)) if call.excinfo: rep = ItemSetupReport(item, call.excinfo, call.outerr) - item.config.api.pytest_itemsetupreport(rep=rep) + item.config.hook.pytest_itemsetupreport(rep=rep) else: call = item.config.guardedcall(lambda: item.runtest()) - item.config.api.pytest_item_runtest_finished( + item.config.hook.pytest_item_runtest_finished( item=item, excinfo=call.excinfo, outerr=call.outerr) call = item.config.guardedcall(lambda: self.teardown_exact(item)) if call.excinfo: rep = ItemSetupReport(item, call.excinfo, call.outerr) - item.config.api.pytest_itemsetupreport(rep=rep) + item.config.hook.pytest_itemsetupreport(rep=rep) def pytest_collector_collect(self, collector): call = item.config.guardedcall(lambda x: collector._memocollect()) @@ -146,7 +146,7 @@ # =============================================================================== def test_generic(plugintester): - plugintester.apicheck(RunnerPlugin()) + plugintester.hookcheck(RunnerPlugin()) class TestSetupState: def test_setup_prepare(self, testdir): Modified: py/trunk/py/test/plugin/pytest_terminal.py ============================================================================== --- py/trunk/py/test/plugin/pytest_terminal.py (original) +++ py/trunk/py/test/plugin/pytest_terminal.py Thu May 7 19:37:45 2009 @@ -61,7 +61,7 @@ self._tw.sep(sep, title, **markup) def getcategoryletterword(self, rep): - res = self.config.api.pytest_report_teststatus(rep=rep) + res = self.config.hook.pytest_report_teststatus(rep=rep) if res: return res for cat in 'skipped failed passed ???'.split(): @@ -228,7 +228,7 @@ if exitstatus in (0, 1, 2): self.summary_failures() self.summary_skips() - self.config.api.pytest_terminal_summary(terminalreporter=self) + self.config.hook.pytest_terminal_summary(terminalreporter=self) if excrepr is not None: self.summary_final_exc(excrepr) if exitstatus == 2: @@ -389,15 +389,15 @@ """) rep = TerminalReporter(modcol.config, file=linecomp.stringio) rep.config.pluginmanager.register(rep) - rep.config.api.pytest_testrunstart() + rep.config.hook.pytest_testrunstart() for item in testdir.genitems([modcol]): ev = runner.basic_run_report(item) - rep.config.api.pytest_itemtestreport(rep=ev) + rep.config.hook.pytest_itemtestreport(rep=ev) linecomp.assert_contains_lines([ "*test_pass_skip_fail.py .sF" ]) - rep.config.api.pytest_testrunfinish(exitstatus=1) + rep.config.hook.pytest_testrunfinish(exitstatus=1) linecomp.assert_contains_lines([ " def test_func():", "> assert 0", @@ -416,21 +416,21 @@ """, configargs=("-v",)) rep = TerminalReporter(modcol.config, file=linecomp.stringio) rep.config.pluginmanager.register(rep) - rep.config.api.pytest_testrunstart() + rep.config.hook.pytest_testrunstart() items = modcol.collect() rep.config.option.debug = True # for item in items: - rep.config.api.pytest_itemstart(item=item, node=None) + rep.config.hook.pytest_itemstart(item=item, node=None) s = linecomp.stringio.getvalue().strip() assert s.endswith(item.name) - rep.config.api.pytest_itemtestreport(rep=runner.basic_run_report(item)) + rep.config.hook.pytest_itemtestreport(rep=runner.basic_run_report(item)) linecomp.assert_contains_lines([ "*test_pass_skip_fail_verbose.py:2: *test_ok*PASS*", "*test_pass_skip_fail_verbose.py:4: *test_skip*SKIP*", "*test_pass_skip_fail_verbose.py:6: *test_func*FAIL*", ]) - rep.config.api.pytest_testrunfinish(exitstatus=1) + rep.config.hook.pytest_testrunfinish(exitstatus=1) linecomp.assert_contains_lines([ " def test_func():", "> assert 0", @@ -441,13 +441,13 @@ modcol = testdir.getmodulecol("import xyz") rep = TerminalReporter(modcol.config, file=linecomp.stringio) rep.config.pluginmanager.register(rep) - rep.config.api.pytest_testrunstart() + rep.config.hook.pytest_testrunstart() l = list(testdir.genitems([modcol])) assert len(l) == 0 linecomp.assert_contains_lines([ "*test_collect_fail.py F*" ]) - rep.config.api.pytest_testrunfinish(exitstatus=1) + rep.config.hook.pytest_testrunfinish(exitstatus=1) linecomp.assert_contains_lines([ "> import xyz", "E ImportError: No module named xyz" @@ -537,11 +537,11 @@ """, configargs=("--tb=%s" % tbopt,)) rep = TerminalReporter(modcol.config, file=linecomp.stringio) rep.config.pluginmanager.register(rep) - rep.config.api.pytest_testrunstart() + rep.config.hook.pytest_testrunstart() for item in testdir.genitems([modcol]): - rep.config.api.pytest_itemtestreport( + rep.config.hook.pytest_itemtestreport( rep=runner.basic_run_report(item)) - rep.config.api.pytest_testrunfinish(exitstatus=1) + rep.config.hook.pytest_testrunfinish(exitstatus=1) s = linecomp.stringio.getvalue() if tbopt == "long": print s @@ -561,7 +561,7 @@ item = testdir.getitem("def test_func(): pass") rep = TerminalReporter(item.config, file=linecomp.stringio) item.config.pluginmanager.register(rep) - rep.config.api.pytest_itemstart(item=item) + rep.config.hook.pytest_itemstart(item=item) linecomp.assert_contains_lines([ "*test_show_path_before_running_test.py*" ]) @@ -578,10 +578,10 @@ #""", configargs=("--showskipsummary",) + ("-v",)*verbose) rep = TerminalReporter(modcol.config, file=linecomp.stringio) modcol.config.pluginmanager.register(rep) - modcol.config.api.pytest_testrunstart() + modcol.config.hook.pytest_testrunstart() try: for item in testdir.genitems([modcol]): - modcol.config.api.pytest_itemtestreport( + modcol.config.hook.pytest_itemtestreport( rep=runner.basic_run_report(item)) except KeyboardInterrupt: excinfo = py.code.ExceptionInfo() @@ -590,7 +590,7 @@ s = linecomp.stringio.getvalue() if not verbose: assert s.find("_keyboard_interrupt.py Fs") != -1 - modcol.config.api.pytest_testrunfinish(exitstatus=2, excrepr=excinfo.getrepr()) + modcol.config.hook.pytest_testrunfinish(exitstatus=2, excrepr=excinfo.getrepr()) text = linecomp.stringio.getvalue() linecomp.assert_contains_lines([ " def test_foobar():", @@ -640,16 +640,16 @@ rep = CollectonlyReporter(modcol.config, out=linecomp.stringio) modcol.config.pluginmanager.register(rep) indent = rep.indent - rep.config.api.pytest_collectstart(collector=modcol) + rep.config.hook.pytest_collectstart(collector=modcol) linecomp.assert_contains_lines([ "" ]) item = modcol.join("test_func") - rep.config.api.pytest_itemstart(item=item) + rep.config.hook.pytest_itemstart(item=item) linecomp.assert_contains_lines([ " ", ]) - rep.config.api.pytest_collectreport( + rep.config.hook.pytest_collectreport( rep=runner.CollectReport(modcol, [], excinfo=None)) assert rep.indent == indent @@ -687,6 +687,6 @@ assert repr_pythonversion() == str(x) def test_generic(plugintester): - plugintester.apicheck(TerminalPlugin) - plugintester.apicheck(TerminalReporter) - plugintester.apicheck(CollectonlyReporter) + plugintester.hookcheck(TerminalPlugin) + plugintester.hookcheck(TerminalReporter) + plugintester.hookcheck(CollectonlyReporter) Modified: py/trunk/py/test/plugin/pytest_tmpdir.py ============================================================================== --- py/trunk/py/test/plugin/pytest_tmpdir.py (original) +++ py/trunk/py/test/plugin/pytest_tmpdir.py Thu May 7 19:37:45 2009 @@ -24,7 +24,7 @@ # =============================================================================== # def test_generic(plugintester): - plugintester.apicheck(TmpdirPlugin) + plugintester.hookcheck(TmpdirPlugin) def test_funcarg(testdir): item = testdir.getitem("def test_func(tmpdir): pass") Modified: py/trunk/py/test/plugin/pytest_unittest.py ============================================================================== --- py/trunk/py/test/plugin/pytest_unittest.py (original) +++ py/trunk/py/test/plugin/pytest_unittest.py Thu May 7 19:37:45 2009 @@ -71,7 +71,7 @@ def test_generic(plugintester): - plugintester.apicheck(UnittestPlugin) + plugintester.hookcheck(UnittestPlugin) def test_simple_unittest(testdir): testpath = testdir.makepyfile(""" Modified: py/trunk/py/test/plugin/pytest_xfail.py ============================================================================== --- py/trunk/py/test/plugin/pytest_xfail.py (original) +++ py/trunk/py/test/plugin/pytest_xfail.py Thu May 7 19:37:45 2009 @@ -57,7 +57,7 @@ # =============================================================================== def test_generic(plugintester): - plugintester.apicheck(XfailPlugin) + plugintester.hookcheck(XfailPlugin) def test_xfail(plugintester, linecomp): testdir = plugintester.testdir() Modified: py/trunk/py/test/pluginmanager.py ============================================================================== --- py/trunk/py/test/pluginmanager.py (original) +++ py/trunk/py/test/pluginmanager.py Thu May 7 19:37:45 2009 @@ -12,17 +12,17 @@ self.MultiCall = self.comregistry.MultiCall self.impname2plugin = {} - self.api = py._com.PluginAPI( - apiclass=api.PluginHooks, + self.hook = py._com.Hooks( + hookspecs=api.PluginHooks, registry=self.comregistry) def register(self, plugin): - self.api.pytest_plugin_registered(plugin=plugin) + self.hook.pytest_plugin_registered(plugin=plugin) import types self.comregistry.register(plugin) def unregister(self, plugin): - self.api.pytest_plugin_unregistered(plugin=plugin) + self.hook.pytest_plugin_unregistered(plugin=plugin) self.comregistry.unregister(plugin) def isregistered(self, plugin): @@ -95,7 +95,7 @@ if excinfo is None: excinfo = py.code.ExceptionInfo() excrepr = excinfo.getrepr(funcargs=True, showlocals=True) - return self.api.pytest_internalerror(excrepr=excrepr) + return self.hook.pytest_internalerror(excrepr=excrepr) def do_addoption(self, parser): methods = self.comregistry.listattr("pytest_addoption", reverse=True) @@ -115,12 +115,12 @@ assert not hasattr(self, '_config') config.pluginmanager.register(self) self._config = config - config.api.pytest_configure(config=self._config) + config.hook.pytest_configure(config=self._config) def do_unconfigure(self, config): config = self._config del self._config - config.api.pytest_unconfigure(config=config) + config.hook.pytest_unconfigure(config=config) config.pluginmanager.unregister(self) def do_itemrun(self, item, pdb=None): Modified: py/trunk/py/test/pycollect.py ============================================================================== --- py/trunk/py/test/pycollect.py (original) +++ py/trunk/py/test/pycollect.py Thu May 7 19:37:45 2009 @@ -135,7 +135,7 @@ return self.join(name) def makeitem(self, name, obj): - res = self.config.api.pytest_pymodule_makeitem( + res = self.config.hook.pytest_pymodule_makeitem( modcol=self, name=name, obj=obj) if res: return res @@ -345,7 +345,7 @@ def runtest(self): """ execute the given test function. """ - self.config.api.pytest_pyfunc_call(pyfuncitem=self, + self.config.hook.pytest_pyfunc_call(pyfuncitem=self, args=self._args, kwargs=self.funcargs) def setup(self): Modified: py/trunk/py/test/runner.py ============================================================================== --- py/trunk/py/test/runner.py (original) +++ py/trunk/py/test/runner.py Thu May 7 19:37:45 2009 @@ -32,7 +32,7 @@ raise except: excinfo = py.code.ExceptionInfo() - testrep = item.config.api.pytest_item_makereport( + testrep = item.config.hook.pytest_item_makereport( item=item, excinfo=excinfo, when=when, outerr=outerr) if pdb and testrep.failed: tw = py.io.TerminalWriter() Modified: py/trunk/py/test/session.py ============================================================================== --- py/trunk/py/test/session.py (original) +++ py/trunk/py/test/session.py Thu May 7 19:37:45 2009 @@ -37,16 +37,16 @@ if isinstance(next, Item): remaining = self.filteritems([next]) if remaining: - self.config.api.pytest_itemstart(item=next) + self.config.hook.pytest_itemstart(item=next) yield next else: assert isinstance(next, Collector) - self.config.api.pytest_collectstart(collector=next) + self.config.hook.pytest_collectstart(collector=next) rep = basic_collect_report(next) if rep.passed: for x in self.genitems(rep.result, keywordexpr): yield x - self.config.api.pytest_collectreport(rep=rep) + self.config.hook.pytest_collectreport(rep=rep) if self.shouldstop: break @@ -66,7 +66,7 @@ continue remaining.append(colitem) if deselected: - self.config.api.pytest_deselected(items=deselected) + self.config.hook.pytest_deselected(items=deselected) if self.config.option.keyword.endswith(":"): self._nomatch = True return remaining @@ -78,7 +78,7 @@ def sessionstarts(self): """ setup any neccessary resources ahead of the test run. """ - self.config.api.pytest_testrunstart() + self.config.hook.pytest_testrunstart() def pytest_itemtestreport(self, rep): if rep.failed: @@ -89,7 +89,7 @@ def sessionfinishes(self, exitstatus=0, excinfo=None): """ teardown any resources after a test run. """ - self.config.api.pytest_testrunfinish( + self.config.hook.pytest_testrunfinish( exitstatus=exitstatus, excrepr=excinfo and excinfo.getrepr() or None ) Modified: py/trunk/py/test/testing/test_api.py ============================================================================== --- py/trunk/py/test/testing/test_api.py (original) +++ py/trunk/py/test/testing/test_api.py Thu May 7 19:37:45 2009 @@ -11,4 +11,4 @@ return True config.pluginmanager.register(MyPlugin1()) config.pluginmanager.register(MyPlugin2()) - config.api.pytest_pyfunc_call(pyfuncitem=item) + config.hook.pytest_pyfunc_call(pyfuncitem=item) Modified: py/trunk/py/test/testing/test_pluginmanager.py ============================================================================== --- py/trunk/py/test/testing/test_pluginmanager.py (original) +++ py/trunk/py/test/testing/test_pluginmanager.py Thu May 7 19:37:45 2009 @@ -84,7 +84,7 @@ #syspath.prepend(aplugin.dirpath()) py.std.sys.path.insert(0, str(aplugin.dirpath())) pluginmanager.consider_module(mod) - call = sorter.getcall(pluginmanager.api.pytest_plugin_registered.name) + call = sorter.getcall(pluginmanager.hook.pytest_plugin_registered.name) assert call.plugin.__class__.__name__ == "APlugin" # check that it is not registered twice From hpk at codespeak.net Fri May 8 18:52:45 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Fri, 8 May 2009 18:52:45 +0200 (CEST) Subject: [py-svn] r65167 - in py/trunk/py: path/svn path/svn/testing test test/plugin test/testing Message-ID: <20090508165245.52217169E2F@codespeak.net> Author: hpk Date: Fri May 8 18:52:42 2009 New Revision: 65167 Modified: py/trunk/py/path/svn/svncommon.py py/trunk/py/path/svn/testing/test_urlcommand.py py/trunk/py/path/svn/urlcommand.py py/trunk/py/test/plugin/api.py py/trunk/py/test/plugin/pytest_pytester.py py/trunk/py/test/plugin/pytest_terminal.py py/trunk/py/test/plugin/pytest_unittest.py py/trunk/py/test/pycollect.py py/trunk/py/test/testing/test_pluginmanager.py Log: merge from mercurial re-fix delayed test reporting with tests fix windows issue with cleanup of badchar rename of hook Modified: py/trunk/py/path/svn/svncommon.py ============================================================================== --- py/trunk/py/path/svn/svncommon.py (original) +++ py/trunk/py/path/svn/svncommon.py Fri May 8 18:52:42 2009 @@ -35,6 +35,17 @@ return True return False +def checkbadchars(url): + # (hpk) not quite sure about the exact purpose, guido w.? + proto, uri = url.split("://", 1) + if proto != "file": + host, uripath = uri.split('/', 1) + # only check for bad chars in the non-protocol parts + if (_check_for_bad_chars(host, ALLOWED_CHARS_HOST) \ + or _check_for_bad_chars(uripath, ALLOWED_CHARS)): + raise ValueError("bad char in %r" % (url, )) + + #_______________________________________________________________ class SvnPathBase(common.FSPathBase): 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 Fri May 8 18:52:42 2009 @@ -124,4 +124,4 @@ assert info.kind == 'dir' def test_badchars(): - py.test.raises(ValueError, "py.path.svnurl('file:///tmp/@@@:')") + py.test.raises(ValueError, "py.path.svnurl('http://host/tmp/@@@:')") Modified: py/trunk/py/path/svn/urlcommand.py ============================================================================== --- py/trunk/py/path/svn/urlcommand.py (original) +++ py/trunk/py/path/svn/urlcommand.py Fri May 8 18:52:42 2009 @@ -28,13 +28,7 @@ rev = path.rev auth = path.auth path = path.strpath - proto, uri = path.split("://", 1) - host, uripath = uri.split('/', 1) - # only check for bad chars in the non-protocol parts - if (svncommon._check_for_bad_chars(host, svncommon.ALLOWED_CHARS_HOST) - or svncommon._check_for_bad_chars(uripath, - svncommon.ALLOWED_CHARS)): - raise ValueError("bad char in path %s" % (path, )) + svncommon.checkbadchars(path) path = path.rstrip('/') self.strpath = path self.rev = rev Modified: py/trunk/py/test/plugin/api.py ============================================================================== --- py/trunk/py/test/plugin/api.py (original) +++ py/trunk/py/test/plugin/api.py Fri May 8 18:52:42 2009 @@ -49,9 +49,9 @@ def pytest_collect_directory(self, path, parent): """ return Collection node or None. """ - def pytest_pymodule_makeitem(self, modcol, name, obj): + def pytest_pycollect_obj(self, collector, name, obj): """ return custom item/collector for a python object in a module, or None. """ - pytest_pymodule_makeitem.firstresult = True + pytest_pycollect_obj.firstresult = True def pytest_collectstart(self, collector): """ collector starts collecting. """ Modified: py/trunk/py/test/plugin/pytest_pytester.py ============================================================================== --- py/trunk/py/test/plugin/pytest_pytester.py (original) +++ py/trunk/py/test/plugin/pytest_pytester.py Fri May 8 18:52:42 2009 @@ -224,8 +224,10 @@ if not hasattr(py.std, 'subprocess'): py.test.skip("no subprocess module") env = os.environ.copy() - env['PYTHONPATH'] = "%s:%s" % (os.getcwd(), env['PYTHONPATH']) + env['PYTHONPATH'] = ":".join(filter(None, [ + str(os.getcwd()), env.get('PYTHONPATH', '')])) kw['env'] = env + #print "env", env return py.std.subprocess.Popen(cmdargs, stdout=stdout, stderr=stderr, **kw) def run(self, *cmdargs): Modified: py/trunk/py/test/plugin/pytest_terminal.py ============================================================================== --- py/trunk/py/test/plugin/pytest_terminal.py (original) +++ py/trunk/py/test/plugin/pytest_terminal.py Fri May 8 18:52:42 2009 @@ -136,24 +136,6 @@ self.config.option.traceconfig and category.find("config") != -1: self.write_line("[%s] %s" %(category, msg)) - def pytest_itemstart(self, item, node=None): - if self.config.option.debug: - line = self._metainfoline(item) - extra = "" - if node: - extra = "-> " + str(node.gateway.id) - self.write_ensure_prefix(line, extra) - # in dist situations itemstart (currently only means we - # queued the item for testing, doesn't tell much - elif self.config.option.verbose and self.config.option.dist == "no": - # ensure that the path is printed before the 1st test of - # a module starts running - line = self._metainfoline(item) - self.write_ensure_prefix(line, "") - else: - fspath, lineno, msg = item.metainfo() - self.write_fspath_result(fspath, "") - def pytest_rescheduleitems(self, items): if self.config.option.debug: self.write_sep("!", "RESCHEDULING %s " %(items,)) @@ -161,6 +143,27 @@ def pytest_deselected(self, items): self.stats.setdefault('deselected', []).append(items) + + def pytest_itemstart(self, item, node=None): + if self.config.option.dist != "no": + # for dist-testing situations itemstart means we + # queued the item for sending, not interesting (unless debugging) + if self.config.option.debug: + line = self._metainfoline(item) + extra = "" + if node: + extra = "-> " + str(node.gateway.id) + self.write_ensure_prefix(line, extra) + else: + if self.config.option.verbose: + line = self._metainfoline(item) + self.write_ensure_prefix(line, "") + else: + # ensure that the path is printed before the + # 1st test of a module starts running + fspath, lineno, msg = item.metainfo() + self.write_fspath_result(fspath, "") + def pytest_itemtestreport(self, rep): fspath = rep.colitem.fspath cat, letter, word = self.getcategoryletterword(rep) @@ -170,6 +173,7 @@ markup = {} self.stats.setdefault(cat, []).append(rep) if not self.config.option.verbose: + fspath, lineno, msg = rep.colitem.metainfo() self.write_fspath_result(fspath, letter) else: line = self._metainfoline(rep.colitem) @@ -559,13 +563,33 @@ def test_show_path_before_running_test(self, testdir, linecomp): item = testdir.getitem("def test_func(): pass") - rep = TerminalReporter(item.config, file=linecomp.stringio) - item.config.pluginmanager.register(rep) - rep.config.hook.pytest_itemstart(item=item) + tr = TerminalReporter(item.config, file=linecomp.stringio) + item.config.pluginmanager.register(tr) + tr.config.hook.pytest_itemstart(item=item) linecomp.assert_contains_lines([ "*test_show_path_before_running_test.py*" ]) + def test_itemreport_metainfo(self, testdir, linecomp): + testdir.makeconftest(""" + import py + class Function(py.test.collect.Function): + def metainfo(self): + return "ABCDE", 42, "custom" + """) + item = testdir.getitem("def test_func(): pass") + tr = TerminalReporter(item.config, file=linecomp.stringio) + item.config.pluginmanager.register(tr) + tr.config.hook.pytest_itemstart(item=item) + linecomp.assert_contains_lines([ + "*ABCDE " + ]) + tr.config.option.verbose = True + tr.config.hook.pytest_itemstart(item=item) + linecomp.assert_contains_lines([ + "*ABCDE:43: custom*" + ]) + def pseudo_keyboard_interrupt(self, testdir, linecomp, verbose=False): modcol = testdir.getmodulecol(""" def test_foobar(): Modified: py/trunk/py/test/plugin/pytest_unittest.py ============================================================================== --- py/trunk/py/test/plugin/pytest_unittest.py (original) +++ py/trunk/py/test/plugin/pytest_unittest.py Fri May 8 18:52:42 2009 @@ -18,9 +18,9 @@ class UnittestPlugin: """ discover and integrate traditional ``unittest.py`` tests. """ - def pytest_pymodule_makeitem(self, modcol, name, obj): + def pytest_pycollect_obj(self, collector, name, obj): if py.std.inspect.isclass(obj) and issubclass(obj, py.std.unittest.TestCase): - return UnitTestCase(name, parent=modcol) + return UnitTestCase(name, parent=collector) class UnitTestCase(py.test.collect.Class): def collect(self): Modified: py/trunk/py/test/pycollect.py ============================================================================== --- py/trunk/py/test/pycollect.py (original) +++ py/trunk/py/test/pycollect.py Fri May 8 18:52:42 2009 @@ -135,8 +135,8 @@ return self.join(name) def makeitem(self, name, obj): - res = self.config.hook.pytest_pymodule_makeitem( - modcol=self, name=name, obj=obj) + res = self.config.hook.pytest_pycollect_obj( + collector=self, name=name, obj=obj) if res: return res if (self.classnamefilter(name)) and \ Modified: py/trunk/py/test/testing/test_pluginmanager.py ============================================================================== --- py/trunk/py/test/testing/test_pluginmanager.py (original) +++ py/trunk/py/test/testing/test_pluginmanager.py Fri May 8 18:52:42 2009 @@ -36,8 +36,6 @@ plugin = py.test.config.pluginmanager.getplugin('x500') assert plugin is not None """) - new = str(x500.dirpath()) # "%s:%s" %(x500.dirpath(), os.environ.get('PYTHONPATH', '')) - monkeypatch.setitem(os.environ, 'PYTHONPATH', new) monkeypatch.setitem(os.environ, 'PYTEST_PLUGINS', 'pytest_x500') result = testdir.runpytest(p) assert result.ret == 0 From py-svn at codespeak.net Sat May 9 22:12:42 2009 From: py-svn at codespeak.net (Skye Winsky) Date: Sat, 9 May 2009 22:12:42 +0200 (CEST) Subject: [py-svn] Huge 89% discount by Dr's Winsky Message-ID: <20090509201242.43FEB169F01@codespeak.net> An HTML attachment was scrubbed... URL: From py-svn at codespeak.net Sun May 10 20:49:50 2009 From: py-svn at codespeak.net (Doyle Bonaccorsi) Date: Sun, 10 May 2009 21:49:50 +0300 Subject: [py-svn] Looking for you all day Message-ID: <20090510185240.BF3D816854D@codespeak.net> Overnight delivery of products for men's ardor http://www.befqacuc.cn/ -------------- next part -------------- An HTML attachment was scrubbed... URL: From hpk at codespeak.net Mon May 11 19:31:55 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Mon, 11 May 2009 19:31:55 +0200 (CEST) Subject: [py-svn] r65224 - in py/trunk: doc/test py py/execnet py/execnet/testing py/misc/testing py/test py/test/dist/testing py/test/plugin py/test/testing Message-ID: <20090511173155.97E93168489@codespeak.net> Author: hpk Date: Mon May 11 19:31:54 2009 New Revision: 65224 Added: py/trunk/py/test/funcargs.py Modified: py/trunk/doc/test/config.txt py/trunk/doc/test/funcargs.txt py/trunk/doc/test/test.txt py/trunk/py/_com.py py/trunk/py/execnet/gateway.py py/trunk/py/execnet/testing/test_gwmanage.py py/trunk/py/misc/testing/test_com.py py/trunk/py/test/dist/testing/test_nodemanage.py py/trunk/py/test/plugin/api.py py/trunk/py/test/plugin/pytest_recwarn.py py/trunk/py/test/plugin/pytest_restdoc.py py/trunk/py/test/plugin/pytest_terminal.py py/trunk/py/test/plugin/pytest_tmpdir.py py/trunk/py/test/pycollect.py py/trunk/py/test/testing/test_funcargs.py py/trunk/py/test/testing/test_pickling.py py/trunk/py/test/testing/test_pycollect.py Log: merging 9e880f747f29 (trunk) tip - new funcarg generators - small fixes and cleanups Modified: py/trunk/doc/test/config.txt ============================================================================== --- py/trunk/doc/test/config.txt (original) +++ py/trunk/doc/test/config.txt Mon May 11 19:31:54 2009 @@ -28,8 +28,10 @@ ------------------------------------------- ``py.test`` runs provide means to create per-test session -temporary (sub) directories. You can create such directories -like this: +temporary (sub) directories through the config object. +You can create directories like this: + +.. XXX use a more local example, just with "config" .. sourcecode: python Modified: py/trunk/doc/test/funcargs.txt ============================================================================== --- py/trunk/doc/test/funcargs.txt (original) +++ py/trunk/doc/test/funcargs.txt Mon May 11 19:31:54 2009 @@ -1,52 +1,76 @@ ====================================================== -**funcargs**: powerful and simple test setup +**funcargs**: powerful test setup and parametrization ====================================================== -In version 1.0 py.test introduces a new mechanism for setting up test -state for use by Python test functions. It is particularly useful -for functional and integration testing but also for unit testing. -Using funcargs you can easily: - -* write self-contained, simple to read and debug test functions -* cleanly encapsulate glue code between your app and your tests -* setup test state depending on command line options or environment - -Using the funcargs mechanism will increase readability -and allow for easier refactoring of your application -and its test suites. +Since version 1.0 it is possible to provide arguments to test functions, +often called "funcargs". The funcarg mechanisms were developed with +these goals in mind: + +* **no boilerplate**: cleanly encapsulate test setup and fixtures +* **flexibility**: easily setup test state depending on command line options or environment +* **readability**: write simple to read and debug test functions +* **parametrizing tests**: run a test function multiple times with different parameters + .. contents:: Contents: :depth: 2 -The basic funcarg request/provide mechanism +Basic mechanisms by example ============================================= -To use funcargs you only need to specify -a named argument for your test function: +providing single function arguments as needed +--------------------------------------------------------- + +Let's look at a simple example of using funcargs within a test module: .. sourcecode:: python - def test_function(myarg): - # use myarg + def pytest_funcarg__myfuncarg(request): + return 42 + + def test_function(myfuncarg): + assert myfuncarg == 42 + +1. To setup the running of the ``test_function()`` call, py.test + looks up a provider for the ``myfuncarg`` argument. + The provider method is recognized by its ``pytest_funcarg__`` prefix + followed by the requested function argument name. + The `request object`_ gives access to test context. -For each test function that requests this ``myarg`` -argument a matching so called funcarg provider -will be invoked. A Funcarg provider for ``myarg`` -is written down liks this: +2. A ``test_function(42)`` call is executed. If the test fails + one can see the original provided value. + + +generating test runs with multiple function argument values +---------------------------------------------------------------------- + +You can parametrize multiple runs of a test function by +providing multiple values for function arguments. Here +is an example for running the same test function three times. .. sourcecode:: python - def pytest_funcarg__myarg(self, request): - # return value for myarg here + def pytest_genfuncruns(runspec): + if "arg1" in runspec.funcargnames: + runspec.addfuncarg("arg1", 10) + runspec.addfuncarg("arg1", 20) + runspec.addfuncarg("arg1", 30) + + def test_function(arg1): + assert myfuncarg in (10, 20, 30) + +Here is what happens: -Such a provider method can live on a test class, -test module or on a local or global plugin. -The method is recognized by the ``pytest_funcarg__`` -prefix and is correlated to the argument -name which follows this prefix. The passed in -``request`` object allows to interact -with test configuration, test collection -and test running aspects. +1. The ``pytest_genfuncruns()`` hook will be called once for each test + function. The if-statement makes sure that we only add function + arguments (and runs) for functions that need it. The `runspec object`_ + provides access to context information. + +2. Subsequently the ``test_function()`` will be called three times + with three different values for ``arg1``. + +Funcarg rules and support objects +==================================== .. _`request object`: @@ -65,11 +89,13 @@ ``request.function``: python function object requesting the argument -``request.fspath``: filesystem path of containing module +``request.cls``: class object where the test function is defined in or None. + +``runspec.module``: module object where the test function is defined in. ``request.config``: access to command line opts and general config -finalizing after test function executed +cleanup after test function execution ++++++++++++++++++++++++++++++++++++++++ Request objects allow to **register a finalizer method** which is @@ -86,33 +112,8 @@ request.addfinalizer(lambda: myfile.close()) return myfile -a unique temporary directory -++++++++++++++++++++++++++++++++++++++++ - -request objects allow to create unique temporary -directories. These directories will be created -as subdirectories under the `per-testsession -temporary directory`_. Each request object -receives its own unique subdirectory whose -basenames starts with the name of the function -that triggered the funcarg request. You -can further work with the provided `py.path.local`_ -object to e.g. create subdirs or config files:: - - def pytest_funcarg__mysetup(self, request): - tmpdir = request.maketempdir() - tmpdir.mkdir("mysubdir") - tmpdir.join("config.ini").write("[default") - return tmpdir - -Note that you do not need to perform finalization, -i.e. remove the temporary directory as this is -part of the global management of the base temporary -directory. -.. _`per-testsession temporary directory`: config.html#basetemp - -decorating/adding to existing funcargs +decorating other funcarg providers ++++++++++++++++++++++++++++++++++++++++ If you want to **decorate a function argument** that is @@ -131,33 +132,73 @@ for a use of this method. -.. _`funcarg lookup order`: - -Order of funcarg provider lookup ----------------------------------------- +.. _`lookup order`: -For any funcarg argument request here is the -lookup order for provider methods: +Order of provider and test generator lookup +---------------------------------------------- -1. test class (if we are executing a method) -2. test module -3. local plugins -4. global plugins +Both test generators as well as funcarg providers +are looked up in the following three scopes: +1. test module +2. local plugins +3. global plugins Using multiple funcargs ---------------------------------------- -A test function may receive more than one -function arguments. For each of the -function arguments a lookup of a -matching provider will be performed. +Test functions can have multiple arguments +which can either come from a test generator +or from a provider. +.. _`runspec object`: + +runspec objects +------------------------ -Funcarg Tutorial Examples -============================ +Runspecs help to inspect a testfunction and +to generate tests with combinations of function argument values. -tutorial example: the "test/app-specific" setup pattern +generating and combining funcargs ++++++++++++++++++++++++++++++++++++++++++++++++++++ + +Calling ``runspec.addfuncarg(argname, value)`` will trigger +tests function calls with the given function +argument value. For each already existing +funcarg combination, the added funcarg value will + +* be merged to the existing funcarg combination if the + new argument name isn't part of the funcarg combination yet. + +* otherwise generate a new test call where the existing + funcarg combination is copied and updated + with the newly added funcarg value. + +For simple usage, e.g. test functions with a single +generated function argument, each call to ``addfuncarg`` +will just trigger a new call. + +This scheme allows two sources to generate +function arguments independently from each other. + +Attributes of runspec objects +++++++++++++++++++++++++++++++++++++++++ + +``runspec.funcargnames``: set of required function arguments for given function + +``runspec.function``: underlying python test function + +``runspec.cls``: class object where the test function is defined in or None. + +``runspec.module``: the module object where the test function is defined in. + +``runspec.config``: access to command line opts and general config + + +Useful Funcarg Tutorial Examples +======================================= + +application specific test setup --------------------------------------------------------- Here is a basic useful step-wise example for handling application @@ -202,7 +243,7 @@ return MyApp() py.test finds the ``pytest_funcarg__mysetup`` method by -name, see `funcarg lookup order`_ for more on this mechanism. +name, see also `lookup order`_. To run the example we put a pseudo MyApp object into ``myapp.py``: @@ -265,29 +306,8 @@ conn = mysetup.getsshconnection() # work with conn -Running this without the command line will yield this run result:: - - XXX fill in - - -Example: specifying funcargs in test modules or classes ---------------------------------------------------------- - -.. sourcecode:: python - - def pytest_funcarg__mysetup(request): - result = request.call_next_provider() - result.extra = "..." - return result - -You can put such a function into a test class like this: - -.. sourcecode:: python - - class TestClass: - def pytest_funcarg__mysetup(self, request): - # ... - # +Running this without specifying a command line option will result in a skipped +test_function. .. _`accept example`: @@ -309,16 +329,12 @@ def __init__(self, request): if not request.config.option.acceptance: py.test.skip("specify -A to run acceptance tests") - self.tmpdir = request.config.maketempdir(request.argname) - self._old = self.tmpdir.chdir() - request.addfinalizer(self.finalize) - - def run(self): - return py.process.cmdexec("echo hello") - - def finalize(self): - self._old.chdir() - # cleanup any other resources + self.tmpdir = request.config.mktemp(request.function.__name__, numbered=True) + + def run(self, cmd): + """ called by test code to execute an acceptance test. """ + self.tmpdir.chdir() + return py.process.cmdexec(cmd) and the actual test function example: @@ -327,48 +343,116 @@ def test_some_acceptance_aspect(accept): accept.tmpdir.mkdir("somesub") - result = accept.run() - assert result + result = accept.run("ls -la") + assert "somesub" in result -That's it! This test will get automatically skipped with -an appropriate message if you just run ``py.test``:: - - ... OUTPUT of py.test on this example ... - +If you run this test without specifying a command line option +the test will get skipped with an appropriate message. Otherwise +you can start to add convenience and test support methods +to your AcceptFuncarg and drive running of tools or +applications and provide ways to do assertions about +the output. .. _`decorator example`: -example: decorating/extending a funcarg in a TestClass +example: decorating a funcarg in a test module -------------------------------------------------------------- For larger scale setups it's sometimes useful to decorare -a funcarg just for a particular test module or even -a particular test class. We can extend the `accept example`_ -by putting this in our test class: +a funcarg just for a particular test module. We can +extend the `accept example`_ by putting this in our test class: .. sourcecode:: python - class TestSpecialAcceptance: - def pytest_funcarg__accept(self, request): - arg = request.call_next_provider() - # create a special layout in our tempdir - arg.tmpdir.mkdir("special") - return arg + def pytest_funcarg__accept(self, request): + arg = request.call_next_provider() + # create a special layout in our tempdir + arg.tmpdir.mkdir("special") + return arg + class TestSpecialAcceptance: def test_sometest(self, accept): assert accept.tmpdir.join("special").check() -According to the `funcarg lookup order`_ our class-specific provider will -be invoked first. Here, we just ask our request object to -call the next provider and decorate its result. This simple +According to the the `lookup order`_ our module level provider +will be invoked first and it can ask ask its request object to +call the next provider and then decorate its result. This mechanism allows us to stay ignorant of how/where the function argument is provided. -Note that we make use here of `py.path.local`_ objects -that provide uniform access to the local filesystem. +sidenote: the temporary directory used here are instances of +the `py.path.local`_ class which provides many of the os.path +methods in a convenient way. .. _`py.path.local`: ../path.html#local +.. _`combine multiple funcarg values`: + + +parametrize test functions by combining generated funcargs +-------------------------------------------------------------------------- + +Adding different funcargs will generate test calls with +all combinations of added funcargs. Consider this example: + +.. sourcecode:: python + + def makearg1(runspec): + runspec.addfuncarg("arg1", 10) + runspec.addfuncarg("arg1", 11) + + def makearg2(runspec): + runspec.addfuncarg("arg2", 20) + runspec.addfuncarg("arg2", 21) + + def pytest_genfuncruns(runspec): + makearg1(runspec) + makearg2(runspec) + + # the actual test function + + def test_function(arg1, arg2): + assert arg1 in (10, 20) + assert arg2 in (20, 30) + +Running this test module will result in ``test_function`` +being called four times, in the following order:: + + test_function(10, 20) + test_function(10, 21) + test_function(11, 20) + test_function(11, 21) + + +example: test functions with generated and provided funcargs +------------------------------------------------------------------- + +You can mix generated function arguments and normally +provided ones. Consider this module: + +.. sourcecode:: python + + def pytest_genfuncruns(runspec): + if "arg1" in runspec.funcargnames: # test_function2 does not have it + runspec.addfuncarg("arg1", 10) + runspec.addfuncarg("arg1", 20) + + def pytest_funcarg__arg2(request): + return [10, 20] + + def test_function(arg1, arg2): + assert arg1 in arg2 + + def test_function2(arg2): + assert args2 == [10, 20] + +Running this test module will result in ``test_function`` +being called twice, with these arguments:: + + test_function(10, [10, 20]) + test_function(20, [10, 20]) + + Questions and Answers ================================== @@ -377,14 +461,14 @@ Why ``pytest_funcarg__*`` methods? ------------------------------------ -When experimenting with funcargs we also considered an explicit -registration mechanism, i.e. calling a register method e.g. on the -config object. But lacking a good use case for this indirection and -flexibility we decided to go for `Convention over Configuration`_ -and allow to directly specify the provider. It has the -positive implication that you should be able to -"grep" for ``pytest_funcarg__MYARG`` and will find all -providing sites (usually exactly one). +When experimenting with funcargs we also +considered an explicit registration mechanism, i.e. calling a register +method on the config object. But lacking a good use case for this +indirection and flexibility we decided to go for `Convention over +Configuration`_ and allow to directly specify the provider. It has the +positive implication that you should be able to "grep" for +``pytest_funcarg__MYARG`` and will find all providing sites (usually +exactly one). .. _`Convention over Configuration`: http://en.wikipedia.org/wiki/Convention_over_Configuration Modified: py/trunk/doc/test/test.txt ============================================================================== --- py/trunk/doc/test/test.txt (original) +++ py/trunk/doc/test/test.txt Mon May 11 19:31:54 2009 @@ -11,14 +11,18 @@ features_: a walk through basic features and usage. +funcargs_: powerful parametrized test function setup + +`distributed testing`_: distribute test runs to other machines and platforms. + plugins_: using available plugins. extend_: writing plugins and advanced configuration. -`distributed testing`_ how to distribute test runs to other machines and platforms. .. _quickstart: quickstart.html .. _features: features.html +.. _funcargs: funcargs.html .. _plugins: plugins.html .. _extend: ext.html .. _`distributed testing`: dist.html Modified: py/trunk/py/_com.py ============================================================================== --- py/trunk/py/_com.py (original) +++ py/trunk/py/_com.py Mon May 11 19:31:54 2009 @@ -123,10 +123,14 @@ return "" %(self._hookspecs, self._plugins) class HookCall: - def __init__(self, registry, name, firstresult): + def __init__(self, registry, name, firstresult, extralookup=None): self.registry = registry self.name = name self.firstresult = firstresult + self.extralookup = extralookup and [extralookup] or () + + def clone(self, extralookup): + return HookCall(self.registry, self.name, self.firstresult, extralookup) def __repr__(self): mode = self.firstresult and "firstresult" or "each" @@ -136,7 +140,8 @@ if args: raise TypeError("only keyword arguments allowed " "for api call to %r" % self.name) - mc = MultiCall(self.registry.listattr(self.name), **kwargs) + attr = self.registry.listattr(self.name, extra=self.extralookup) + mc = MultiCall(attr, **kwargs) return mc.execute(firstresult=self.firstresult) comregistry = Registry() Modified: py/trunk/py/execnet/gateway.py ============================================================================== --- py/trunk/py/execnet/gateway.py (original) +++ py/trunk/py/execnet/gateway.py Mon May 11 19:31:54 2009 @@ -167,7 +167,8 @@ except IOError: self._trace('IOError on _stopsend()') self._channelfactory._finished_receiving() - self._trace('leaving %r' % threading.currentThread()) + if threading: # might be None during shutdown/finalization + self._trace('leaving %r' % threading.currentThread()) from sys import exc_info def _send(self, msg): Modified: py/trunk/py/execnet/testing/test_gwmanage.py ============================================================================== --- py/trunk/py/execnet/testing/test_gwmanage.py (original) +++ py/trunk/py/execnet/testing/test_gwmanage.py Mon May 11 19:31:54 2009 @@ -114,7 +114,7 @@ class pytest_funcarg__mysetup: def __init__(self, request): - tmp = request.maketempdir() + tmp = request.config.mktemp(request.function.__name__, numbered=True) self.source = tmp.mkdir("source") self.dest = tmp.mkdir("dest") Modified: py/trunk/py/misc/testing/test_com.py ============================================================================== --- py/trunk/py/misc/testing/test_com.py (original) +++ py/trunk/py/misc/testing/test_com.py Mon May 11 19:31:54 2009 @@ -190,3 +190,23 @@ class Api: pass mcm = Hooks(hookspecs=Api) assert mcm.registry == py._com.comregistry + + def test_hooks_extra_plugins(self): + registry = Registry() + class Api: + def hello(self, arg): + pass + hook_hello = Hooks(hookspecs=Api, registry=registry).hello + class Plugin: + def hello(self, arg): + return arg + 1 + registry.register(Plugin()) + class Plugin2: + def hello(self, arg): + return arg + 2 + newhook = hook_hello.clone(extralookup=Plugin2()) + l = newhook(arg=3) + assert l == [5, 4] + l2 = hook_hello(arg=3) + assert l2 == [4] + Modified: py/trunk/py/test/dist/testing/test_nodemanage.py ============================================================================== --- py/trunk/py/test/dist/testing/test_nodemanage.py (original) +++ py/trunk/py/test/dist/testing/test_nodemanage.py Mon May 11 19:31:54 2009 @@ -3,8 +3,9 @@ class pytest_funcarg__mysetup: def __init__(self, request): - basetemp = request.maketempdir() - basetemp = basetemp.mkdir(request.function.__name__) + basetemp = request.config.mktemp( + "mysetup:%s" % request.function.__name__, + numbered=True) self.source = basetemp.mkdir("source") self.dest = basetemp.mkdir("dest") Added: py/trunk/py/test/funcargs.py ============================================================================== --- (empty file) +++ py/trunk/py/test/funcargs.py Mon May 11 19:31:54 2009 @@ -0,0 +1,115 @@ +import py + +def getfuncargnames(function): + argnames = py.std.inspect.getargs(function.func_code)[0] + startindex = hasattr(function, 'im_self') and 1 or 0 + numdefaults = len(function.func_defaults or ()) + if numdefaults: + return argnames[startindex:-numdefaults] + return argnames[startindex:] + +def fillfuncargs(function): + """ fill missing funcargs. """ + if function._args: + # functions yielded from a generator: we don't want + # to support that because we want to go here anyway: + # http://bitbucket.org/hpk42/py-trunk/issue/2/next-generation-generative-tests + pass + else: + # standard Python Test function/method case + for argname in getfuncargnames(function.obj): + if argname not in function.funcargs: + request = FuncargRequest(pyfuncitem=function, argname=argname) + try: + function.funcargs[argname] = request.call_next_provider() + except request.Error: + request._raiselookupfailed() + +class RunSpecs: + def __init__(self, function, config=None, cls=None, module=None): + self.config = config + self.module = module + self.function = function + self.funcargnames = getfuncargnames(function) + self.cls = cls + self.module = module + self._combinations = [] + + def addfuncarg(self, argname, value): + if argname not in self.funcargnames: + raise ValueError("function %r has no funcarg %r" %( + self.function, argname)) + newcombi = [] + if not self._combinations: + newcombi.append({argname:value}) + else: + for combi in self._combinations: + if argname in combi: + combi = combi.copy() + newcombi.append(combi) + combi[argname] = value + self._combinations.extend(newcombi) + +class FunctionCollector(py.test.collect.Collector): + def __init__(self, name, parent, combinations): + super(FunctionCollector, self).__init__(name, parent) + self.combinations = combinations + self.obj = getattr(self.parent.obj, name) + + def collect(self): + l = [] + for i, funcargs in py.builtin.enumerate(self.combinations): + function = self.parent.Function(name="%s[%s]" %(self.name, i), + parent=self, funcargs=funcargs, callobj=self.obj) + l.append(function) + return l + +class FuncargRequest: + _argprefix = "pytest_funcarg__" + + class Error(LookupError): + """ error on performing funcarg request. """ + + def __init__(self, pyfuncitem, argname): + self._pyfuncitem = pyfuncitem + self.argname = argname + self.function = pyfuncitem.obj + self.module = pyfuncitem.getmodulecollector().obj + self.cls = getattr(self.function, 'im_class', None) + self.config = pyfuncitem.config + self.fspath = pyfuncitem.fspath + self._plugins = self.config.pluginmanager.getplugins() + self._plugins.append(pyfuncitem.getmodulecollector().obj) + self._provider = self.config.pluginmanager.listattr( + plugins=self._plugins, + attrname=self._argprefix + str(argname) + ) + + def __repr__(self): + return "" %(self.argname, self._pyfuncitem) + + def call_next_provider(self): + if not self._provider: + raise self.Error("no provider methods left") + next_provider = self._provider.pop() + return next_provider(request=self) + + def addfinalizer(self, finalizer): + self._pyfuncitem.addfinalizer(finalizer) + + def _raiselookupfailed(self): + available = [] + for plugin in self._plugins: + for name in vars(plugin.__class__): + if name.startswith(self._argprefix): + name = name[len(self._argprefix):] + if name not in available: + available.append(name) + fspath, lineno, msg = self._pyfuncitem.metainfo() + line = "%s:%s" %(fspath, lineno) + msg = "funcargument %r not found for: %s" %(self.argname, line) + msg += "\n available funcargs: %s" %(", ".join(available),) + raise LookupError(msg) + + + Modified: py/trunk/py/test/plugin/api.py ============================================================================== --- py/trunk/py/test/plugin/api.py (original) +++ py/trunk/py/test/plugin/api.py Mon May 11 19:31:54 2009 @@ -53,13 +53,15 @@ """ return custom item/collector for a python object in a module, or None. """ pytest_pycollect_obj.firstresult = True + def pytest_genfuncruns(self, runspec): + """ generate (multiple) parametrized calls to a test function.""" + def pytest_collectstart(self, collector): """ collector starts collecting. """ def pytest_collectreport(self, rep): """ collector finished collecting. """ - # ------------------------------------------------------------------------------ # runtest related hooks # ------------------------------------------------------------------------------ Modified: py/trunk/py/test/plugin/pytest_recwarn.py ============================================================================== --- py/trunk/py/test/plugin/pytest_recwarn.py (original) +++ py/trunk/py/test/plugin/pytest_recwarn.py Mon May 11 19:31:54 2009 @@ -1,8 +1,7 @@ """ -"recwarn" funcarg plugin that helps to assert -that warnings are shown to a user. See the test -at the bottom for an example. +provides "recwarn" funcarg for asserting warnings to be shown +to a user. See the test at the bottom for an example. """ import py Modified: py/trunk/py/test/plugin/pytest_restdoc.py ============================================================================== --- py/trunk/py/test/plugin/pytest_restdoc.py (original) +++ py/trunk/py/test/plugin/pytest_restdoc.py Mon May 11 19:31:54 2009 @@ -142,7 +142,7 @@ directives.register_directive('sourcecode', pygments_directive) def resolve_linkrole(self, name, text, check=True): - apigen_relpath = self.project.hookgen_relpath + apigen_relpath = self.project.apigen_relpath if name == 'api': if text == 'py': Modified: py/trunk/py/test/plugin/pytest_terminal.py ============================================================================== --- py/trunk/py/test/plugin/pytest_terminal.py (original) +++ py/trunk/py/test/plugin/pytest_terminal.py Mon May 11 19:31:54 2009 @@ -207,9 +207,10 @@ msg += " -- " + str(sys.executable) self.write_line(msg) - rev = py.__pkg__.getrev() - self.write_line("using py lib: %s " % ( - py.path.local(py.__file__).dirpath(), rev)) + if self.config.option.debug or self.config.option.traceconfig: + rev = py.__pkg__.getrev() + self.write_line("using py lib: %s " % ( + py.path.local(py.__file__).dirpath(), rev)) if self.config.option.traceconfig: plugins = [] for plugin in self.config.pluginmanager.comregistry: Modified: py/trunk/py/test/plugin/pytest_tmpdir.py ============================================================================== --- py/trunk/py/test/plugin/pytest_tmpdir.py (original) +++ py/trunk/py/test/plugin/pytest_tmpdir.py Mon May 11 19:31:54 2009 @@ -27,9 +27,10 @@ plugintester.hookcheck(TmpdirPlugin) def test_funcarg(testdir): + from py.__.test.funcargs import FuncargRequest item = testdir.getitem("def test_func(tmpdir): pass") plugin = TmpdirPlugin() - p = plugin.pytest_funcarg__tmpdir(item.getrequest("tmpdir")) + p = plugin.pytest_funcarg__tmpdir(FuncargRequest(item, "tmpdir")) assert p.check() bn = p.basename.strip("0123456789-") assert bn.endswith("test_func") Modified: py/trunk/py/test/pycollect.py ============================================================================== --- py/trunk/py/test/pycollect.py (original) +++ py/trunk/py/test/pycollect.py Mon May 11 19:31:54 2009 @@ -20,6 +20,7 @@ from py.__.test.collect import configproperty, warnoldcollect from py.__.code.source import findsource pydir = py.path.local(py.__file__).dirpath() +from py.__.test import funcargs class PyobjMixin(object): def obj(): @@ -37,6 +38,16 @@ def _getobj(self): return getattr(self.parent.obj, self.name) + def getmodulecollector(self): + return self._getparent(Module) + def getclasscollector(self): + return self._getparent(Class) + def _getparent(self, cls): + current = self + while current and not isinstance(current, cls): + current = current.parent + return current + def getmodpath(self, stopatmodule=True, includemodule=False): """ return python path relative to the containing module. """ chain = self.listchain() @@ -150,10 +161,25 @@ if res is not None: return res if obj.func_code.co_flags & 32: # generator function + # XXX deprecation warning return self.Generator(name, parent=self) else: - return self.Function(name, parent=self) + return self._genfunctions(name, obj) + def _genfunctions(self, name, funcobj): + module = self.getmodulecollector().obj + # due to _buildname2items funcobj is the raw function, we need + # to work to get at the class + clscol = self.getclasscollector() + cls = clscol and clscol.obj or None + runspec = funcargs.RunSpecs(funcobj, config=self.config, cls=cls, module=module) + gentesthook = self.config.hook.pytest_genfuncruns.clone(extralookup=module) + gentesthook(runspec=runspec) + if not runspec._combinations: + return self.Function(name, parent=self) + return funcargs.FunctionCollector(name=name, + parent=self, combinations=runspec._combinations) + class Module(py.test.collect.File, PyCollectorMixin): def _getobj(self): return self._memoizedcall('_obj', self._importtestmodule) @@ -320,11 +346,13 @@ """ a Function Item is responsible for setting up and executing a Python callable test object. """ - def __init__(self, name, parent=None, config=None, args=(), callobj=_dummy): + def __init__(self, name, parent=None, config=None, args=(), funcargs=None, callobj=_dummy): super(Function, self).__init__(name, parent, config=config) self._finalizers = [] self._args = args - self.funcargs = {} + if funcargs is None: + funcargs = {} + self.funcargs = funcargs if callobj is not _dummy: self._obj = callobj @@ -350,31 +378,7 @@ def setup(self): super(Function, self).setup() - self._setupfuncargs() - - def _setupfuncargs(self): - if self._args: - # functions yielded from a generator: we don't want - # to support that because we want to go here anyway: - # http://bitbucket.org/hpk42/py-trunk/issue/2/next-generation-generative-tests - pass - else: - # standard Python Test function/method case - funcobj = self.obj - startindex = getattr(funcobj, 'im_self', None) and 1 or 0 - argnames = py.std.inspect.getargs(self.obj.func_code)[0] - for i, argname in py.builtin.enumerate(argnames): - if i < startindex: - continue - request = self.getrequest(argname) - try: - self.funcargs[argname] = request.call_next_provider() - except request.Error: - numdefaults = len(funcobj.func_defaults or ()) - if i + numdefaults >= len(argnames): - continue # our args have defaults XXX issue warning? - else: - request._raiselookupfailed() + funcargs.fillfuncargs(self) def __eq__(self, other): try: @@ -385,74 +389,7 @@ except AttributeError: pass return False + def __ne__(self, other): return not self == other - def getrequest(self, argname): - return FuncargRequest(pyfuncitem=self, argname=argname) - - -class FuncargRequest: - _argprefix = "pytest_funcarg__" - - class Error(LookupError): - """ error on performing funcarg request. """ - - def __init__(self, pyfuncitem, argname): - # XXX make pyfuncitem _pyfuncitem - self._pyfuncitem = pyfuncitem - self.argname = argname - self.function = pyfuncitem.obj - self.config = pyfuncitem.config - self.fspath = pyfuncitem.fspath - self._plugins = self._getplugins() - self._methods = self.config.pluginmanager.listattr( - plugins=self._plugins, - attrname=self._argprefix + str(argname) - ) - - def __repr__(self): - return "" %(self.argname, self._pyfuncitem) - - - def _getplugins(self): - plugins = [] - current = self._pyfuncitem - while not isinstance(current, Module): - current = current.parent - if isinstance(current, (Instance, Module)): - plugins.insert(0, current.obj) - return self.config.pluginmanager.getplugins() + plugins - - def call_next_provider(self): - if not self._methods: - raise self.Error("no provider methods left") - nextmethod = self._methods.pop() - return nextmethod(request=self) - - def addfinalizer(self, finalizer): - self._pyfuncitem.addfinalizer(finalizer) - - def maketempdir(self): - basetemp = self.config.getbasetemp() - tmp = py.path.local.make_numbered_dir( - prefix=self.function.__name__ + "_", - keep=0, rootdir=basetemp) - return tmp - - def _raiselookupfailed(self): - available = [] - for plugin in self._plugins: - for name in vars(plugin.__class__): - if name.startswith(self._argprefix): - name = name[len(self._argprefix):] - if name not in available: - available.append(name) - fspath, lineno, msg = self._pyfuncitem.metainfo() - line = "%s:%s" %(fspath, lineno) - msg = "funcargument %r not found for: %s" %(self.argname, line) - msg += "\n available funcargs: %s" %(", ".join(available),) - raise LookupError(msg) - - - Modified: py/trunk/py/test/testing/test_funcargs.py ============================================================================== --- py/trunk/py/test/testing/test_funcargs.py (original) +++ py/trunk/py/test/testing/test_funcargs.py Mon May 11 19:31:54 2009 @@ -1,6 +1,22 @@ import py +from py.__.test import funcargs -class TestFuncargs: +def test_getfuncargnames(): + def f(): pass + assert not funcargs.getfuncargnames(f) + def g(arg): pass + assert funcargs.getfuncargnames(g) == ['arg'] + def h(arg1, arg2="hello"): pass + assert funcargs.getfuncargnames(h) == ['arg1'] + def h(arg1, arg2, arg3="hello"): pass + assert funcargs.getfuncargnames(h) == ['arg1', 'arg2'] + class A: + def f(self, arg1, arg2="hello"): + pass + assert funcargs.getfuncargnames(A().f) == ['arg1'] + assert funcargs.getfuncargnames(A.f) == ['arg1'] + +class TestFillFuncArgs: def test_funcarg_lookupfails(self, testdir): testdir.makeconftest(""" class ConftestPlugin: @@ -8,7 +24,7 @@ return 42 """) item = testdir.getitem("def test_func(some): pass") - exc = py.test.raises(LookupError, "item._setupfuncargs()") + exc = py.test.raises(LookupError, "funcargs.fillfuncargs(item)") s = str(exc.value) assert s.find("xyzsomething") != -1 @@ -18,20 +34,8 @@ def pytest_funcarg__some(self, request): return request.function.__name__ item.config.pluginmanager.register(Provider()) - item._setupfuncargs() - assert len(item.funcargs) == 1 - - def test_funcarg_lookup_default_gets_overriden(self, testdir): - item = testdir.getitem("def test_func(some=42, other=13): pass") - class Provider: - def pytest_funcarg__other(self, request): - return request.function.__name__ - item.config.pluginmanager.register(Provider()) - item._setupfuncargs() + funcargs.fillfuncargs(item) assert len(item.funcargs) == 1 - name, value = item.funcargs.popitem() - assert name == "other" - assert value == item.name def test_funcarg_basic(self, testdir): item = testdir.getitem("def test_func(some, other): pass") @@ -41,7 +45,7 @@ def pytest_funcarg__other(self, request): return 42 item.config.pluginmanager.register(Provider()) - item._setupfuncargs() + funcargs.fillfuncargs(item) assert len(item.funcargs) == 2 assert item.funcargs['some'] == "test_func" assert item.funcargs['other'] == 42 @@ -58,9 +62,9 @@ pass """) item1, item2 = testdir.genitems([modcol]) - item1._setupfuncargs() + funcargs.fillfuncargs(item1) assert item1.funcargs['something'] == "test_method" - item2._setupfuncargs() + funcargs.fillfuncargs(item2) assert item2.funcargs['something'] == "test_func" class TestRequest: @@ -69,37 +73,44 @@ def pytest_funcarg__something(request): pass def test_func(something): pass """) - req = item.getrequest("other") + req = funcargs.FuncargRequest(item, argname="other") assert req.argname == "other" assert req.function == item.obj + assert hasattr(req.module, 'test_func') + assert req.cls is None assert req.function.__name__ == "test_func" assert req.config == item.config assert repr(req).find(req.function.__name__) != -1 + + def test_request_attributes_method(self, testdir): + item, = testdir.getitems(""" + class TestB: + def test_func(self, something): + pass + """) + req = funcargs.FuncargRequest(item, argname="something") + assert req.cls.__name__ == "TestB" - def test_request_contains_funcargs_methods(self, testdir): + def test_request_contains_funcargs_provider(self, testdir): modcol = testdir.getmodulecol(""" def pytest_funcarg__something(request): pass class TestClass: - def pytest_funcarg__something(self, request): - pass def test_method(self, something): pass """) item1, = testdir.genitems([modcol]) assert item1.name == "test_method" - methods = item1.getrequest("something")._methods - assert len(methods) == 2 - method1, method2 = methods - assert not hasattr(method1, 'im_self') - assert method2.im_self is not None + provider = funcargs.FuncargRequest(item1, "something")._provider + assert len(provider) == 1 + assert provider[0].__name__ == "pytest_funcarg__something" def test_request_call_next_provider(self, testdir): item = testdir.getitem(""" def pytest_funcarg__something(request): pass def test_func(something): pass """) - req = item.getrequest("something") + req = funcargs.FuncargRequest(item, "something") val = req.call_next_provider() assert val is None py.test.raises(req.Error, "req.call_next_provider()") @@ -109,22 +120,147 @@ def pytest_funcarg__something(request): pass def test_func(something): pass """) - req = item.getrequest("something") + req = funcargs.FuncargRequest(item, "something") l = [1] req.addfinalizer(l.pop) item.teardown() - def test_request_maketemp(self, testdir): - item = testdir.getitem("def test_func(): pass") - req = item.getrequest("xxx") - tmpdir = req.maketempdir() - tmpdir2 = req.maketempdir() - assert tmpdir != tmpdir2 - assert tmpdir.basename.startswith("test_func") - assert tmpdir2.basename.startswith("test_func") - def test_request_getmodulepath(self, testdir): modcol = testdir.getmodulecol("def test_somefunc(): pass") item, = testdir.genitems([modcol]) - req = item.getrequest("hello") + req = funcargs.FuncargRequest(item, "xxx") assert req.fspath == modcol.fspath + +class TestRunSpecs: + def test_no_funcargs(self, testdir): + def function(): pass + runspec = funcargs.RunSpecs(function) + assert not runspec.funcargnames + + def test_function_basic(self): + def func(arg1, arg2="qwe"): pass + runspec = funcargs.RunSpecs(func) + assert len(runspec.funcargnames) == 1 + assert 'arg1' in runspec.funcargnames + assert runspec.function is func + assert runspec.cls is None + + def test_addfuncarg_basic(self): + def func(arg1): pass + runspec = funcargs.RunSpecs(func) + py.test.raises(ValueError, """ + runspec.addfuncarg("notexists", 100) + """) + runspec.addfuncarg("arg1", 100) + assert len(runspec._combinations) == 1 + assert runspec._combinations[0] == {'arg1': 100} + + def test_addfuncarg_two(self): + def func(arg1): pass + runspec = funcargs.RunSpecs(func) + runspec.addfuncarg("arg1", 100) + runspec.addfuncarg("arg1", 101) + assert len(runspec._combinations) == 2 + assert runspec._combinations[0] == {'arg1': 100} + assert runspec._combinations[1] == {'arg1': 101} + + def test_addfuncarg_combined(self): + runspec = funcargs.RunSpecs(lambda arg1, arg2: 0) + runspec.addfuncarg('arg1', 1) + runspec.addfuncarg('arg1', 2) + runspec.addfuncarg('arg2', 100) + combinations = runspec._combinations + assert len(combinations) == 2 + assert combinations[0] == {'arg1': 1, 'arg2': 100} + assert combinations[1] == {'arg1': 2, 'arg2': 100} + runspec.addfuncarg('arg2', 101) + assert len(combinations) == 4 + assert combinations[-1] == {'arg1': 2, 'arg2': 101} + +class TestGenfuncFunctional: + def test_attributes(self, testdir): + p = testdir.makepyfile(""" + import py + def pytest_genfuncruns(runspec): + runspec.addfuncarg("runspec", runspec) + + def test_function(runspec): + assert runspec.config == py.test.config + assert runspec.module.__name__ == __name__ + assert runspec.function == test_function + assert runspec.cls is None + class TestClass: + def test_method(self, runspec): + assert runspec.config == py.test.config + assert runspec.module.__name__ == __name__ + # XXX actually have the unbound test function here? + assert runspec.function == TestClass.test_method.im_func + assert runspec.cls == TestClass + """) + result = testdir.runpytest(p, "-v") + result.stdout.fnmatch_lines([ + "*2 passed in*", + ]) + + def test_arg_twice(self, testdir): + testdir.makeconftest(""" + class ConftestPlugin: + def pytest_genfuncruns(self, runspec): + assert "arg" in runspec.funcargnames + runspec.addfuncarg("arg", 10) + runspec.addfuncarg("arg", 20) + """) + p = testdir.makepyfile(""" + def test_myfunc(arg): + assert arg == 10 + """) + result = testdir.runpytest("-v", p) + assert result.stdout.fnmatch_lines([ + "*test_myfunc*PASS*", # case for 10 + "*test_myfunc*FAIL*", # case for 20 + "*1 failed, 1 passed*" + ]) + + def test_two_functions(self, testdir): + p = testdir.makepyfile(""" + def pytest_genfuncruns(runspec): + runspec.addfuncarg("arg1", 10) + runspec.addfuncarg("arg1", 20) + + def test_func1(arg1): + assert arg1 == 10 + def test_func2(arg1): + assert arg1 in (10, 20) + """) + result = testdir.runpytest("-v", p) + assert result.stdout.fnmatch_lines([ + "*test_func1*0*PASS*", + "*test_func1*1*FAIL*", + "*test_func2*PASS*", + "*1 failed, 3 passed*" + ]) + + def test_genfuncarg_inmodule(self, testdir): + testdir.makeconftest(""" + class ConftestPlugin: + def pytest_genfuncruns(self, runspec): + assert "arg" in runspec.funcargnames + runspec.addfuncarg("arg", 10) + """) + p = testdir.makepyfile(""" + def pytest_genfuncruns(runspec): + runspec.addfuncarg("arg2", 10) + runspec.addfuncarg("arg2", 20) + runspec.addfuncarg("classarg", 17) + + class TestClass: + def test_myfunc(self, arg, arg2, classarg): + assert classarg == 17 + assert arg == arg2 + """) + result = testdir.runpytest("-v", p) + assert result.stdout.fnmatch_lines([ + "*test_myfunc*0*PASS*", + "*test_myfunc*1*FAIL*", + "*1 failed, 1 passed*" + ]) Modified: py/trunk/py/test/testing/test_pickling.py ============================================================================== --- py/trunk/py/test/testing/test_pickling.py (original) +++ py/trunk/py/test/testing/test_pickling.py Mon May 11 19:31:54 2009 @@ -34,9 +34,9 @@ p2config._initafterpickle(config.topdir) return p2config -class TestImmutablePickling: - pytest_funcarg__pickletransport = ImmutablePickleTransport +pytest_funcarg__pickletransport = ImmutablePickleTransport +class TestImmutablePickling: def test_pickle_config(self, testdir, pickletransport): config1 = testdir.parseconfig() assert config1.topdir == testdir.tmpdir Modified: py/trunk/py/test/testing/test_pycollect.py ============================================================================== --- py/trunk/py/test/testing/test_pycollect.py (original) +++ py/trunk/py/test/testing/test_pycollect.py Mon May 11 19:31:54 2009 @@ -215,6 +215,12 @@ assert not skipped and not failed class TestFunction: + def test_getmodulecollector(self, testdir): + item = testdir.getitem("def test_func(): pass") + modcol = item.getmodulecollector() + assert isinstance(modcol, py.test.collect.Module) + assert hasattr(modcol.obj, 'test_func') + def test_function_equality(self, tmpdir): config = py.test.config._reparse([tmpdir]) f1 = py.test.collect.Function(name="name", config=config, From py-svn at codespeak.net Tue May 12 01:46:41 2009 From: py-svn at codespeak.net (Luigi Delnegro) Date: Tue, 12 May 2009 01:46:41 +0200 (CEST) Subject: [py-svn] Astounding! 70% off Message-ID: <20090511234641.B6F1A168434@codespeak.net> An HTML attachment was scrubbed... URL: From py-svn at codespeak.net Sat May 16 10:28:46 2009 From: py-svn at codespeak.net (Stephane Meir) Date: Sat, 16 May 2009 15:28:46 +0700 Subject: [py-svn] Invite #5559648 Message-ID: <5559648851.9018.8.camel@law> Don't overpay for a brand name! Use our site to get discounts http://www.injqoiox.com -------------- next part -------------- An HTML attachment was scrubbed... URL: From hpk at codespeak.net Wed May 20 15:14:04 2009 From: hpk at codespeak.net (hpk at codespeak.net) Date: Wed, 20 May 2009 15:14:04 +0200 (CEST) Subject: [py-svn] r65325 - py/extradoc/talk/ep2009 Message-ID: <20090520131404.9C3DC169F3D@codespeak.net> Author: hpk Date: Wed May 20 15:14:04 2009 New Revision: 65325 Added: py/extradoc/talk/ep2009/ py/extradoc/talk/ep2009/tutorial-advanced.txt - copied, changed from r63218, py/extradoc/talk/pycon-us-2009/proposal-pytest-advanced.txt Log: ep tutorial talks as submitted Copied: py/extradoc/talk/ep2009/tutorial-advanced.txt (from r63218, py/extradoc/talk/pycon-us-2009/proposal-pytest-advanced.txt) ============================================================================== --- py/extradoc/talk/pycon-us-2009/proposal-pytest-advanced.txt (original) +++ py/extradoc/talk/ep2009/tutorial-advanced.txt Wed May 20 15:14:04 2009 @@ -1,73 +1,73 @@ -Title: py.test - cross-platform and distributed testing -Presenter: Holger Krekel , Brian +Title: rapid testing with py.test +Presenter: Holger Krekel Tutorial format: interactive lecture -Recording: I give permission to record and publish my PyCon tutorial for free distribution. Intended Audience: Python programmers -Maximum number of students: maybe 30 +Maximum number of students: 30 Perequisites/knowledge: good knowledge of python programming, basic familiarity with automated testing -Requirements: Attendees are welcome to bring their laptops with Python installed (version 2.4 or higher). -Notes to reviewer: visting the beginner-oriented tutorial "rapid testing with minimal effort" is recommended, but not required. +Requirements: Attendees should bring their laptops with Python installed (version 2.4 or higher). Tutorial Summary: -Want to know more about advanced automated testing with Python? -Use a tool that allows you to ad-hoc distribute tests to multiple -CPUs for speed and to multiple platforms for compatibility checks? -With tons of debugging help in failure situations? - -This tutorial provides in-depth information on advanced usages -of the popular py.test tool. We highlight its current feature set -including using and writing extensions for generating HTML pages, -testing Javascript or ReST documents. We showcase and discuss ways -of distributing tests across CPUs and platforms and will leave -time to discuss and tackle specific scenarios brought up -during the session. +Want to dive deeper into automated testing with Python? +Want to know how to rapidly write all kinds of tests +and organise your test suite with minimal boilerplate? +Want to use a tool that allows you to ad-hoc +distribute tests to multiple machines and platforms? + +This tutorial provides in-depth information about writing +tests in Python and running them with py.test. +We walk through basic consecutive examples, the current +feature set and exercise ways to setup and configure +custom test runs. In the second part we distribute +tests to multiple CPUs and platforms and discuss +the basics of extension mechanism. + +The tutorial format will be an interactive lecture with +some exercises and plenty of time for questions. Please +bring your laptop and have Python installed and working. -The tutorial format will be an interactive lecture with plenty -of time for questions. +Draft Outline: -Outline for review: +Intro: quick round on backgrounds of participants and talker -Terminology/Overview (20 minutes) +Terminology/Overview (20 minutes) - developer and "customer" tests - small/medium/big tests aka unit/functional/integration - acceptance tests - benefits of automated testing -- existing python testing tools -Walkthrough py.test basic features (30 minutes) -- example of test module -- working with failures, tracebacks -- generative tests -- skipping chunks within doctests -- looponfailing: run large test set until all tests pass - -Using extensions (40 minutes) -- integrate collection/run of traditional unit-tests -- run functions in their own tempdir -- testing ReST documents -- running Javascript tests -- running Prolog tests -- html reporting for nightly runs +Walkthrough Python test functions (60 minutes) +- installing, basic usage of py.test +- test functions +- test classes +- interactive debugging and pdb introspection +- using setup state (fixtures) in your tests +- adding a command line option for test setup +- parametrizing tests with multiple values +- looponfailing: run test set until all tests pass -Break +* break * -Writing extensions (30 minutes) -- overview on extensibility -- per-project hooks for customization -- conftest.py mechanism -- global hooks for plugins -- event system for custom reporting -- test collection hooks -- test running hooks -- writing cross-project plugins +distributing tests (30 minutes) -Distributed testing (45 minutes) -- motivation/vision - run your tests on multiple CPUs/multicore - running tests on multiple machines at once - running tests on windows, driven by Unix - do's and dont's for cross-process testing +testing extensions (20 minutes) +- traditional unit tests +- doctests +- ReST documents +- Javascript tests + +* break * + +Writing extensions (30 minutes) +- hook mechanism +- local and global plugins +- test collection hooks + example +- test running hooks + example + Buffer, Q&A (15 minutes) From commits-noreply at bitbucket.org Thu May 21 14:39:25 2009 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Thu, 21 May 2009 12:39:25 -0000 Subject: [py-svn] commit/py-trunk: 11 new changesets Message-ID: <20090521123925.16212.26563@domU-12-31-39-00-D4-C1.compute-1.internal> 11 new changesets in py-trunk: http://www.bitbucket.org/hpk42/py-trunk/changeset/560438bcd311/ changeset: r1120:560438bcd311 user: hpk date: 2009-05-20 23:12:37 summary: plugin cleanups * make pytest_eventlog.py work again by adding a hack to the registry, rename * disable resultdb hook plugin, it needs merging with resultlog * add some docstrings, streamline bits affected #: 17 files (2.8 KB) http://www.bitbucket.org/hpk42/py-trunk/changeset/79138bf3eccf/ changeset: r1121:79138bf3eccf user: hpk date: 2009-05-21 09:45:43 summary: * create funcarg Request object only once per function run setup * add getfuncargvalue() for retrieving arbitrary funcargs from a provider affected #: 5 files (2.2 KB) http://www.bitbucket.org/hpk42/py-trunk/changeset/684245b8f49a/ changeset: r1122:684245b8f49a user: hpk date: 2009-05-21 09:45:43 summary: * streamline hookrecording and global py._com.comregistry patching * this fixes a reporting bug where events from inner test runs would mix with the outer test run affected #: 7 files (975 bytes) http://www.bitbucket.org/hpk42/py-trunk/changeset/6964323e83c1/ changeset: r1123:6964323e83c1 user: hpk date: 2009-05-21 09:54:48 summary: update todo to make some sense again shift samuele in license file affected #: 2 files (6.2 KB) http://www.bitbucket.org/hpk42/py-trunk/changeset/187e48282ba1/ changeset: r1124:187e48282ba1 user: hpk date: 2009-05-21 14:33:09 summary: rename sorter to reprec (report-recorder) affected #: 16 files (2 bytes) http://www.bitbucket.org/hpk42/py-trunk/changeset/f85d7faed81c/ changeset: r1125:f85d7faed81c user: hpk date: 2009-05-21 14:33:15 summary: * xspec: define str(xspec) to give back the original specification string * fix a test invocation affected #: 3 files (103 bytes) http://www.bitbucket.org/hpk42/py-trunk/changeset/b53f04b00154/ changeset: r1126:b53f04b00154 user: hpk date: 2009-05-21 14:36:52 summary: fixed examples according to new plugin and setup mechanisms affected #: 4 files (240 bytes) http://www.bitbucket.org/hpk42/py-trunk/changeset/6589bc04ac45/ changeset: r1127:6589bc04ac45 user: hpk date: 2009-05-21 14:37:30 summary: introduce and document "session" scope for finalization helpers affected #: 5 files (1.6 KB) http://www.bitbucket.org/hpk42/py-trunk/changeset/b060e143ade2/ changeset: r1117:b060e143ade2 user: hpk date: 2009-05-19 20:00:24 summary: nicer docstring for monkeypatch funcarg plugin affected #: 1 file (493 bytes) http://www.bitbucket.org/hpk42/py-trunk/changeset/c98ff950a6f9/ changeset: r1118:c98ff950a6f9 user: hpk date: 2009-05-19 20:15:39 summary: adding not implemented test to not forget about it affected #: 1 file (394 bytes) http://www.bitbucket.org/hpk42/py-trunk/changeset/1d3447b35cd9/ changeset: r1119:1d3447b35cd9 user: hpk date: 2009-05-20 22:02:08 summary: remove superflous registry.call_firstresult and NONEASFIRSTRESULT logic affected #: 5 files (1.9 KB) Repository URL: http://bitbucket.org/hpk42/py-trunk/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Thu May 21 15:22:15 2009 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Thu, 21 May 2009 13:22:15 -0000 Subject: [py-svn] commit/py-trunk: hpk: add a very basic test and include _pytest dependency. thanks pedronis. Message-ID: <20090521132215.16212.39049@domU-12-31-39-00-D4-C1.compute-1.internal> 1 new changeset in py-trunk: http://www.bitbucket.org/hpk42/py-trunk/changeset/c69ed9f7459a/ changeset: r1128:c69ed9f7459a user: hpk date: 2009-05-21 15:22:01 summary: add a very basic test and include _pytest dependency. thanks pedronis. affected #: 1 file (305 bytes) Repository URL: http://bitbucket.org/hpk42/py-trunk/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Thu May 21 20:30:03 2009 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Thu, 21 May 2009 18:30:03 -0000 Subject: [py-svn] commit/py-trunk: 4 new changesets Message-ID: <20090521183003.16212.8773@domU-12-31-39-00-D4-C1.compute-1.internal> 4 new changesets in py-trunk: http://www.bitbucket.org/hpk42/py-trunk/changeset/936ab6a808f8/ changeset: r1129:936ab6a808f8 user: hpk date: 2009-05-21 16:16:22 summary: regen setup.py, striking some unused headers affected #: 3 files (518 bytes) http://www.bitbucket.org/hpk42/py-trunk/changeset/5a20fb4e1425/ changeset: r1130:5a20fb4e1425 user: Samuele Pedroni date: 2009-05-21 19:08:33 summary: minimal changes to expose fillfuncargs affected #: 3 files (527 bytes) http://www.bitbucket.org/hpk42/py-trunk/changeset/70337d41ec13/ changeset: r1131:70337d41ec13 user: Samuele Pedroni date: 2009-05-21 19:36:02 summary: addresses issue15 expose fillfuncargs as py.test.collect._fillfuncargs instead for now affected #: 2 files (13 bytes) http://www.bitbucket.org/hpk42/py-trunk/changeset/5c9b7cf10a52/ changeset: r1132:5c9b7cf10a52 user: hpk date: 2009-05-21 20:29:37 summary: merging pedronis-branch changes to resolve issue 15 for now. This introduces py.test.collect._fillfuncargs which can operate on Function like objects. affected #: 1 file (75 bytes) Repository URL: http://bitbucket.org/hpk42/py-trunk/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From py-svn at codespeak.net Fri May 22 13:58:44 2009 From: py-svn at codespeak.net (Lueras Collette) Date: Fri, 22 May 2009 13:58:44 +0200 (CEST) Subject: [py-svn] Exams for our group Message-ID: <200905221608.c96ed16@[117.201.80.143]> An HTML attachment was scrubbed... URL: From commits-noreply at bitbucket.org Fri May 22 19:59:06 2009 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Fri, 22 May 2009 17:59:06 -0000 Subject: [py-svn] commit/py-trunk: 4 new changesets Message-ID: <20090522175906.16212.8048@domU-12-31-39-00-D4-C1.compute-1.internal> 4 new changesets in py-trunk: http://www.bitbucket.org/hpk42/py-trunk/changeset/87708310c315/ changeset: r1136:87708310c315 user: hpk date: 2009-05-22 19:57:21 summary: * introduce pytest_pdb: plugin handling --pdb invocation * killing some unused/unneccessary hooks affected #: 11 files (8.2 KB) http://www.bitbucket.org/hpk42/py-trunk/changeset/664685e892f6/ changeset: r1133:664685e892f6 user: hpk date: 2009-05-22 13:01:48 summary: derive Exit from KeyboardInterrupt to simplify exception catching affected #: 3 files (218 bytes) http://www.bitbucket.org/hpk42/py-trunk/changeset/7da95f66cf66/ changeset: r1134:7da95f66cf66 user: hpk date: 2009-05-22 19:56:05 summary: make setupevent tests of pytest_runner.py work affected #: 18 files (6.6 KB) http://www.bitbucket.org/hpk42/py-trunk/changeset/bd1d0e0a3a84/ changeset: r1135:bd1d0e0a3a84 user: hpk date: 2009-05-22 19:57:11 summary: refactor basic_run_report a bit to allow for immediate PDB runs and clearer code. affected #: 1 file (249 bytes) Repository URL: http://bitbucket.org/hpk42/py-trunk/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From py-svn at codespeak.net Sat May 23 15:18:45 2009 From: py-svn at codespeak.net (Fairburn Owiqfyqd) Date: Sat, 23 May 2009 15:18:45 +0200 (CEST) Subject: [py-svn] Read, useful for you Message-ID: <759180579368287.TFNKTXGNVJVAXQN@dslb-092-074-112-189.pools.arcor-ip.net> An HTML attachment was scrubbed... URL: From py-svn at codespeak.net Sat May 23 22:32:15 2009 From: py-svn at codespeak.net (Grunder Ules) Date: Sat, 23 May 2009 22:32:15 +0200 (CEST) Subject: [py-svn] I'm depressed, missing you Message-ID: <743064751352764.XVTCRIAFEWVCOCL@dbs136.neoplus.adsl.tpnet.pl> An HTML attachment was scrubbed... URL: From commits-noreply at bitbucket.org Sun May 24 17:14:29 2009 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Sun, 24 May 2009 15:14:29 -0000 Subject: [py-svn] commit/py-3k: yangyan5: remove the ATEST file Message-ID: <20090524151429.16212.65097@domU-12-31-39-00-D4-C1.compute-1.internal> 1 new changeset in py-3k: http://www.bitbucket.org/yangyan5/py-3k/changeset/1ba49d3ca987/ changeset: r1138:1ba49d3ca987 user: yangyan5 date: 2009-05-24 17:10:16 summary: remove the ATEST file affected #: 1 file (0 bytes) Repository URL: http://bitbucket.org/yangyan5/py-3k/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Sun May 24 17:57:53 2009 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Sun, 24 May 2009 15:57:53 -0000 Subject: [py-svn] commit/py-3k: 2 new changesets Message-ID: <20090524155753.16212.61229@domU-12-31-39-00-D4-C1.compute-1.internal> 2 new changesets in py-3k: http://www.bitbucket.org/yangyan5/py-3k/changeset/19bbd79fc492/ changeset: r1139:19bbd79fc492 user: yangyan5 date: 2009-05-24 17:52:47 summary: replace doctest.py and unittest.py with their 2.6 versions affected #: 1 file (757 bytes) http://www.bitbucket.org/yangyan5/py-3k/changeset/9b818c9e0853/ changeset: r1140:9b818c9e0853 user: yangyan5 date: 2009-05-24 17:55:45 summary: replace doctest.py and unittest.py with their 2.6 versions affected #: 1 file (30.4 KB) Repository URL: http://bitbucket.org/yangyan5/py-3k/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Sun May 24 18:52:14 2009 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Sun, 24 May 2009 16:52:14 -0000 Subject: [py-svn] commit/py-3k: yangyan5: replace tests of doctest.py and unittest.py with their 2.6 versions Message-ID: <20090524165214.16212.54401@domU-12-31-39-00-D4-C1.compute-1.internal> 1 new changeset in py-3k: http://www.bitbucket.org/yangyan5/py-3k/changeset/d2f703743e5d/ changeset: r1141:d2f703743e5d user: yangyan5 date: 2009-05-24 18:51:09 summary: replace tests of doctest.py and unittest.py with their 2.6 versions affected #: 5 files (87.2 KB) Repository URL: http://bitbucket.org/yangyan5/py-3k/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Mon May 25 10:46:47 2009 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Mon, 25 May 2009 08:46:47 -0000 Subject: [py-svn] commit/py-trunk: 4 new changesets Message-ID: <20090525084647.16212.75475@domU-12-31-39-00-D4-C1.compute-1.internal> 4 new changesets in py-trunk: http://www.bitbucket.org/hpk42/py-trunk/changeset/dd99fa53a986/ changeset: r1137:dd99fa53a986 user: hpk date: 2009-05-22 21:53:26 summary: * rename testrunstart/finish to sessionstart and pass it an argument * simplify pyexecnetcleanup plugin affected #: 7 files (652 bytes) http://www.bitbucket.org/hpk42/py-trunk/changeset/afc63fd3465f/ changeset: r1138:afc63fd3465f user: hpk date: 2009-05-22 23:50:35 summary: integrate plugin hook checking directly when registering remove plugintester plugin, all functionality now in testdir affected #: 24 files (5.4 KB) http://www.bitbucket.org/hpk42/py-trunk/changeset/3647d57dea31/ changeset: r1139:3647d57dea31 user: hpk date: 2009-05-22 23:57:08 summary: shift tests and remove superflous files affected #: 3 files (550 bytes) http://www.bitbucket.org/hpk42/py-trunk/changeset/69929b1e9e8f/ changeset: r1140:69929b1e9e8f user: hpk date: 2009-05-25 10:46:04 summary: introduce Function._genid, also used for more correct equality implementation affected #: 4 files (679 bytes) Repository URL: http://bitbucket.org/hpk42/py-trunk/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Mon May 25 14:21:43 2009 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Mon, 25 May 2009 12:21:43 -0000 Subject: [py-svn] commit/py-trunk: hpk: fix a bug that collectonly reporting did not show internal errors (thanks ronny) Message-ID: <20090525122143.16212.42306@domU-12-31-39-00-D4-C1.compute-1.internal> 1 new changeset in py-trunk: http://www.bitbucket.org/hpk42/py-trunk/changeset/f3cdedb7f65f/ changeset: r1141:f3cdedb7f65f user: hpk date: 2009-05-25 14:21:21 summary: fix a bug that collectonly reporting did not show internal errors (thanks ronny) affected #: 1 file (488 bytes) Repository URL: http://bitbucket.org/hpk42/py-trunk/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From py-svn at codespeak.net Tue May 26 13:10:43 2009 From: py-svn at codespeak.net (Paskiewicz G. Maxwell) Date: Tue, 26 May 2009 13:10:43 +0200 (CEST) Subject: [py-svn] Sites' secret area Message-ID: An HTML attachment was scrubbed... URL: From commits-noreply at bitbucket.org Wed May 27 06:56:54 2009 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Wed, 27 May 2009 04:56:54 -0000 Subject: [py-svn] commit/py-3k: 7 new changesets Message-ID: <20090527045654.20024.54306@domU-12-31-39-00-D4-C1.compute-1.internal> 7 new changesets in py-3k: http://www.bitbucket.org/yangyan5/py-3k/changeset/dd99fa53a986/ changeset: r1142:dd99fa53a986 user: hpk date: 2009-05-22 21:53:26 summary: * rename testrunstart/finish to sessionstart and pass it an argument * simplify pyexecnetcleanup plugin affected #: 7 files (652 bytes) http://www.bitbucket.org/yangyan5/py-3k/changeset/afc63fd3465f/ changeset: r1143:afc63fd3465f user: hpk date: 2009-05-22 23:50:35 summary: integrate plugin hook checking directly when registering remove plugintester plugin, all functionality now in testdir affected #: 24 files (5.4 KB) http://www.bitbucket.org/yangyan5/py-3k/changeset/3647d57dea31/ changeset: r1144:3647d57dea31 user: hpk date: 2009-05-22 23:57:08 summary: shift tests and remove superflous files affected #: 3 files (550 bytes) http://www.bitbucket.org/yangyan5/py-3k/changeset/69929b1e9e8f/ changeset: r1145:69929b1e9e8f user: hpk date: 2009-05-25 10:46:04 summary: introduce Function._genid, also used for more correct equality implementation affected #: 4 files (679 bytes) http://www.bitbucket.org/yangyan5/py-3k/changeset/f3cdedb7f65f/ changeset: r1146:f3cdedb7f65f user: hpk date: 2009-05-25 14:21:21 summary: fix a bug that collectonly reporting did not show internal errors (thanks ronny) affected #: 1 file (488 bytes) http://www.bitbucket.org/yangyan5/py-3k/changeset/aebb8d3ad354/ changeset: r1147:aebb8d3ad354 user: yangyan5 date: 2009-05-27 06:46:50 summary: port doctest & unittest from 2.6 to 2.4 affected #: 6 files (12.1 KB) http://www.bitbucket.org/yangyan5/py-3k/changeset/ed9bae49c32e/ changeset: r1148:ed9bae49c32e user: yangyan5 date: 2009-05-27 06:55:05 summary: Merge affected #: 0 files (0 bytes) Repository URL: http://bitbucket.org/yangyan5/py-3k/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Wed May 27 19:50:28 2009 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Wed, 27 May 2009 17:50:28 -0000 Subject: [py-svn] commit/py-3k: yangyan5: restore to original doctest and unittest Message-ID: <20090527175028.20024.1382@domU-12-31-39-00-D4-C1.compute-1.internal> 1 new changeset in py-3k: http://www.bitbucket.org/yangyan5/py-3k/changeset/81dcc250a8b1/ changeset: r1149:81dcc250a8b1 user: yangyan5 date: 2009-05-27 19:50:12 summary: restore to original doctest and unittest affected #: 5 files (10.8 KB) Repository URL: http://bitbucket.org/yangyan5/py-3k/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Wed May 27 19:56:16 2009 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Wed, 27 May 2009 17:56:16 -0000 Subject: [py-svn] commit/py-3k: yangyan5: port doctest from 2.6 to 2.4. test_doctest not finished. Message-ID: <20090527175616.20024.97609@domU-12-31-39-00-D4-C1.compute-1.internal> 1 new changeset in py-3k: http://www.bitbucket.org/yangyan5/py-3k/changeset/7a62fb3dcdde/ changeset: r1150:7a62fb3dcdde user: yangyan5 date: 2009-05-27 19:55:55 summary: port doctest from 2.6 to 2.4. test_doctest not finished. affected #: 9 files (89.4 KB) Repository URL: http://bitbucket.org/yangyan5/py-3k/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From py-svn at codespeak.net Wed May 27 23:01:26 2009 From: py-svn at codespeak.net (Sandie) Date: Wed, 27 May 2009 23:01:26 +0200 (CEST) Subject: [py-svn] Shocking Buffet's advice Message-ID: <343038522301096.CSAEYHGLIERMAAP@[125.129.27.168]> An HTML attachment was scrubbed... URL: From py-svn at codespeak.net Thu May 28 07:25:39 2009 From: py-svn at codespeak.net (py-svn at codespeak.net) Date: Thu, 28 May 2009 07:25:39 +0200 (CEST) Subject: [py-svn] For next week Message-ID: <20090528052539.077F216849C@codespeak.net> An HTML attachment was scrubbed... URL: From commits-noreply at bitbucket.org Thu May 28 09:37:05 2009 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Thu, 28 May 2009 07:37:05 -0000 Subject: [py-svn] commit/py-3k: yangyan5: restore to original doctest & unittest Message-ID: <20090528073705.20024.51395@domU-12-31-39-00-D4-C1.compute-1.internal> 1 new changeset in py-3k: http://www.bitbucket.org/yangyan5/py-3k/changeset/1d4509f6be0c/ changeset: r1151:1d4509f6be0c user: yangyan5 date: 2009-05-28 09:36:41 summary: restore to original doctest & unittest affected #: 3 files (642 bytes) Repository URL: http://bitbucket.org/yangyan5/py-3k/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Thu May 28 09:59:09 2009 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Thu, 28 May 2009 07:59:09 -0000 Subject: [py-svn] commit/py-3k: yangyan5: port doctest and unittest. test_doctest still has 5 failures Message-ID: <20090528075909.20024.96880@domU-12-31-39-00-D4-C1.compute-1.internal> 1 new changeset in py-3k: http://www.bitbucket.org/yangyan5/py-3k/changeset/c127836d7a5c/ changeset: r1152:c127836d7a5c user: yangyan5 date: 2009-05-28 09:58:36 summary: port doctest and unittest. test_doctest still has 5 failures affected #: 32 files (371.4 KB) Repository URL: http://bitbucket.org/yangyan5/py-3k/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From commits-noreply at bitbucket.org Thu May 28 14:36:54 2009 From: commits-noreply at bitbucket.org (commits-noreply at bitbucket.org) Date: Thu, 28 May 2009 12:36:54 -0000 Subject: [py-svn] commit/py-3k: hpk: cleanup/rewrite helpert.py and rename it to hackcompatimport Message-ID: <20090528123654.20024.79457@domU-12-31-39-00-D4-C1.compute-1.internal> 1 new changeset in py-3k: http://www.bitbucket.org/yangyan5/py-3k/changeset/a74cca2aba70/ changeset: r1153:a74cca2aba70 user: hpk date: 2009-05-28 14:35:30 summary: cleanup/rewrite helpert.py and rename it to hackcompatimport don't import the sys-path hack from non-tests affected #: 8 files (431 bytes) Repository URL: http://bitbucket.org/yangyan5/py-3k/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email. From py-svn at codespeak.net Thu May 28 15:08:01 2009 From: py-svn at codespeak.net (Sherron) Date: Thu, 28 May 2009 15:08:01 +0200 (CEST) Subject: [py-svn] Look for my cell around Message-ID: <294454870704562.IODEAAHQMXLNEJA@abwb118.neoplus.adsl.tpnet.pl> An HTML attachment was scrubbed... URL: From py-svn at codespeak.net Fri May 29 00:43:55 2009 From: py-svn at codespeak.net (Darwin) Date: Fri, 29 May 2009 00:43:55 +0200 (CEST) Subject: [py-svn] Hot! Obama got cancer Message-ID: <116106273418669.CFHBGTZNIJQARIS@189-31-86-223.bnut3700.dsl.brasiltelecom.net.br> An HTML attachment was scrubbed... URL: From py-svn at codespeak.net Sat May 30 05:54:28 2009 From: py-svn at codespeak.net (Nadia) Date: Sat, 30 May 2009 05:54:28 +0200 (CEST) Subject: [py-svn] Hey, join our team Message-ID: <277374111273224.VINITBXYNSKKNEM@ato196.neoplus.adsl.tpnet.pl> An HTML attachment was scrubbed... URL: From py-svn at codespeak.net Sun May 31 00:58:53 2009 From: py-svn at codespeak.net (Charlsie) Date: Sun, 31 May 2009 00:58:53 +0200 (CEST) Subject: [py-svn] From trade union activists Message-ID: <266617577883718.AJSOXUWTOSJDMYX@201-34-94-104.jvece702.dsl.brasiltelecom.net.br> An HTML attachment was scrubbed... URL: