From commits-noreply at bitbucket.org Mon Jul 4 02:24:41 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Mon, 04 Jul 2011 00:24:41 -0000 Subject: [py-svn] commit/pytest: gutworth: test files are rewritten in a subprocess Message-ID: <20110704002441.28960.36030@bitbucket02.managed.contegix.com> 1 new changeset in pytest: http://bitbucket.org/hpk42/pytest/changeset/20aeb0b05925/ changeset: 20aeb0b05925 user: gutworth date: 2011-07-04 02:28:48 summary: test files are rewritten in a subprocess affected #: 1 file (60 bytes) --- a/testing/test_assertion.py Wed Jun 29 14:00:13 2011 -0500 +++ b/testing/test_assertion.py Sun Jul 03 19:28:48 2011 -0500 @@ -120,9 +120,12 @@ expl = ' '.join(callequal('foo', 'bar')) assert 'raised in repr()' not in expl - at pytest.mark.skipif("config._assertstate.mode != 'rewrite'") -def test_rewritten(): - assert "@py_builtins" in globals() +def test_rewritten(testdir): + testdir.makepyfile(""" + def test_rewritten(): + assert "@py_builtins" in globals() + """) + assert testdir.runpytest().ret == 0 def test_reprcompare_notin(): detail = plugin.pytest_assertrepr_compare('not in', 'foo', 'aaafoobbb')[1:] Repository URL: https://bitbucket.org/hpk42/pytest/ -- 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 Tue Jul 5 16:43:51 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Tue, 05 Jul 2011 14:43:51 -0000 Subject: [py-svn] commit/pytest: hpk42: fix python2.5 compatibility Message-ID: <20110705144351.24307.20346@bitbucket03.managed.contegix.com> 1 new changeset in pytest: http://bitbucket.org/hpk42/pytest/changeset/af84ecf20e0f/ changeset: af84ecf20e0f user: hpk42 date: 2011-07-05 15:21:08 summary: fix python2.5 compatibility affected #: 5 files (81 bytes) --- a/CHANGELOG Sun Jul 03 19:28:48 2011 -0500 +++ b/CHANGELOG Tue Jul 05 15:21:08 2011 +0200 @@ -1,6 +1,7 @@ Changes between 2.0.3 and 2.1.0.DEV ---------------------------------------------- +- fix issue58 and issue59: new assertion code fixes - merge Benjamin's assertionrewrite branch: now assertions for test modules on python 2.6 and above are done by rewriting the AST and saving the pyc file before the test module is imported. --- a/_pytest/__init__.py Sun Jul 03 19:28:48 2011 -0500 +++ b/_pytest/__init__.py Tue Jul 05 15:21:08 2011 +0200 @@ -1,2 +1,2 @@ # -__version__ = '2.1.0.dev4' +__version__ = '2.1.0.dev5' --- a/_pytest/assertion/__init__.py Sun Jul 03 19:28:48 2011 -0500 +++ b/_pytest/assertion/__init__.py Tue Jul 05 15:21:08 2011 +0200 @@ -7,8 +7,6 @@ from _pytest.monkeypatch import monkeypatch from _pytest.assertion import util -REWRITING_AVAILABLE = "_ast" in sys.builtin_module_names - def pytest_addoption(parser): group = parser.getgroup("debugconfig") group.addoption('--assertmode', action="store", dest="assertmode", @@ -40,8 +38,11 @@ mode = "off" elif mode == "default": mode = "rewrite" - if mode == "on" and not REWRITING_AVAILABLE: - mode = "reinterp" + if mode == "rewrite": + try: + import ast + except ImportError: + mode = "reinterp" if mode != "off": _load_modules(mode) def callbinrepr(op, left, right): --- a/setup.py Sun Jul 03 19:28:48 2011 -0500 +++ b/setup.py Tue Jul 05 15:21:08 2011 +0200 @@ -22,7 +22,7 @@ name='pytest', description='py.test: simple powerful testing with Python', long_description = long_description, - version='2.1.0.dev4', + version='2.1.0.dev5', url='http://pytest.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], --- a/testing/test_assertion.py Sun Jul 03 19:28:48 2011 -0500 +++ b/testing/test_assertion.py Tue Jul 05 15:21:08 2011 +0200 @@ -120,6 +120,7 @@ expl = ' '.join(callequal('foo', 'bar')) assert 'raised in repr()' not in expl + at needsnewassert def test_rewritten(testdir): testdir.makepyfile(""" def test_rewritten(): Repository URL: https://bitbucket.org/hpk42/pytest/ -- 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 Tue Jul 5 17:34:49 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Tue, 05 Jul 2011 15:34:49 -0000 Subject: [py-svn] commit/pytest: hpk42: rename and simplify the assert option: Message-ID: <20110705153449.24308.97923@bitbucket03.managed.contegix.com> 1 new changeset in pytest: http://bitbucket.org/hpk42/pytest/changeset/51185a70d005/ changeset: 51185a70d005 user: hpk42 date: 2011-07-05 17:29:53 summary: rename and simplify the assert option: cmdline usage is now: --assert=rewrite/reinterp/plain there is no conflict detection (don't think that's neccessary) affected #: 5 files (345 bytes) --- a/_pytest/__init__.py Tue Jul 05 15:21:08 2011 +0200 +++ b/_pytest/__init__.py Tue Jul 05 17:29:53 2011 +0200 @@ -1,2 +1,2 @@ # -__version__ = '2.1.0.dev5' +__version__ = '2.1.0.dev6' --- a/_pytest/assertion/__init__.py Tue Jul 05 15:21:08 2011 +0200 +++ b/_pytest/assertion/__init__.py Tue Jul 05 17:29:53 2011 +0200 @@ -9,18 +9,18 @@ def pytest_addoption(parser): group = parser.getgroup("debugconfig") - group.addoption('--assertmode', action="store", dest="assertmode", - choices=("rewrite", "reinterp", "off", "default"), - default="default", metavar="off|reinterp|rewrite", + group.addoption('--assert', action="store", dest="assertmode", + choices=("rewrite", "reinterp", "plain",), + default="rewrite", metavar="MODE", help="""control assertion debugging tools. -'off' performs no assertion debugging. -'reinterp' reinterprets the expressions in asserts to glean information. -'rewrite' (the default) rewrites the assert statements in test modules on import -to provide sub-expression results.""") +'plain' performs no assertion debugging. +'reinterp' reinterprets assert statements after they failed to provide assertion expression information. +'rewrite' (the default) rewrites assert statements in test modules on import +to provide assert expression information. """) group.addoption('--no-assert', action="store_true", default=False, - dest="noassert", help="DEPRECATED equivalent to --assertmode=off") + dest="noassert", help="DEPRECATED equivalent to --assert=plain") group.addoption('--nomagic', action="store_true", default=False, - dest="nomagic", help="DEPRECATED equivalent to --assertmode=off") + dest="nomagic", help="DEPRECATED equivalent to --assert=plain") class AssertionState: """State for the assertion plugin.""" @@ -33,17 +33,13 @@ def pytest_configure(config): mode = config.getvalue("assertmode") if config.getvalue("noassert") or config.getvalue("nomagic"): - if mode not in ("off", "default"): - raise pytest.UsageError("assertion options conflict") - mode = "off" - elif mode == "default": - mode = "rewrite" + mode = "plain" if mode == "rewrite": try: import ast except ImportError: mode = "reinterp" - if mode != "off": + if mode != "plain": _load_modules(mode) def callbinrepr(op, left, right): hook_result = config.hook.pytest_assertrepr_compare( @@ -104,7 +100,8 @@ specifically = "failing tests may report as passing" sys.stderr.write("WARNING: " + specifically + - " because assertions are turned off " + " because assert statements are not executed " + "by the underlying Python interpreter " "(are you using python -O?)\n") pytest_assertrepr_compare = util.assertrepr_compare --- a/doc/assert.txt Tue Jul 05 15:21:08 2011 +0200 +++ b/doc/assert.txt Tue Jul 05 17:29:53 2011 +0200 @@ -23,7 +23,7 @@ $ py.test test_assert1.py ============================= test session starts ============================== - platform darwin -- Python 2.7.0 -- pytest-2.1.0.dev4 + platform linux2 -- Python 2.6.6 -- pytest-2.1.0.dev6 collecting ... collected 1 items test_assert1.py F @@ -37,7 +37,7 @@ E + where 3 = f() test_assert1.py:5: AssertionError - =========================== 1 failed in 0.05 seconds =========================== + =========================== 1 failed in 0.01 seconds =========================== py.test has support for showing the values of the most common subexpressions including calls, attributes, comparisons, and binary and unary @@ -105,7 +105,7 @@ $ py.test test_assert2.py ============================= test session starts ============================== - platform darwin -- Python 2.7.0 -- pytest-2.1.0.dev4 + platform linux2 -- Python 2.6.6 -- pytest-2.1.0.dev6 collecting ... collected 1 items test_assert2.py F @@ -124,7 +124,7 @@ E '5' test_assert2.py:5: AssertionError - =========================== 1 failed in 0.05 seconds =========================== + =========================== 1 failed in 0.01 seconds =========================== Special comparisons are done for a number of cases: @@ -181,7 +181,7 @@ E vals: 1 != 2 test_foocompare.py:8: AssertionError - 1 failed in 0.05 seconds + 1 failed in 0.01 seconds .. _assert-details: .. _`assert introspection`: @@ -212,8 +212,8 @@ py.test rewrites test modules on import. It does this by using an import hook to write a new pyc files. Most of the time this works transparently. However, if you are messing with import yourself, the import hook may interfere. If - this is the case, simply use ``--assertmode=reinterp`` or - ``--assertmode=off``. Additionally, rewriting will fail silently if it cannot + this is the case, simply use ``--assert=reinterp`` or + ``--assert=plain``. Additionally, rewriting will fail silently if it cannot write new pycs, i.e. in a read-only filesystem or a zipfile. If an assert statement has not been rewritten or the Python version is less than @@ -221,7 +221,7 @@ py.test walks the frame of the function containing the assert statement to discover sub-expression results of the failing assert statement. You can force py.test to always use assertion reinterpretation by passing the -``--assertmode=reinterp`` option. +``--assert=reinterp`` option. Assert reinterpretation has a caveat not present with assert rewriting: If evaluating the assert expression has side effects you may get a warning that the @@ -238,11 +238,11 @@ content = f.read() assert content != '...' -All assert introspection can be turned off by passing ``--assertmode=off``. +All assert introspection can be turned off by passing ``--assert=plain``. .. versionadded:: 2.1 Add assert rewriting as an alternate introspection technique. .. versionchanged:: 2.1 - Introduce the ``--assertmode`` option. Deprecate ``--no-assert`` and + Introduce the ``--assert`` option. Deprecate ``--no-assert`` and ``--nomagic``. --- a/setup.py Tue Jul 05 15:21:08 2011 +0200 +++ b/setup.py Tue Jul 05 17:29:53 2011 +0200 @@ -22,7 +22,7 @@ name='pytest', description='py.test: simple powerful testing with Python', long_description = long_description, - version='2.1.0.dev5', + version='2.1.0.dev6', url='http://pytest.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], --- a/testing/test_assertion.py Tue Jul 05 15:21:08 2011 +0200 +++ b/testing/test_assertion.py Tue Jul 05 17:29:53 2011 +0200 @@ -179,26 +179,20 @@ off_options = (("--no-assert",), ("--nomagic",), ("--no-assert", "--nomagic"), - ("--assertmode=off",), - ("--assertmode=off", "--no-assert"), - ("--assertmode=off", "--nomagic"), - ("--assertmode=off," "--no-assert", "--nomagic")) + ("--assert=plain",), + ("--assert=plain", "--no-assert"), + ("--assert=plain", "--nomagic"), + ("--assert=plain", "--no-assert", "--nomagic")) for opt in off_options: result = testdir.runpytest(*opt) assert "3 == 4" not in result.stdout.str() - for mode in "rewrite", "reinterp": - for other_opt in off_options[:3]: - opt = ("--assertmode=" + mode,) + other_opt - result = testdir.runpytest(*opt) - assert result.ret == 3 - assert "assertion options conflict" in result.stderr.str() def test_old_assert_mode(testdir): testdir.makepyfile(""" def test_in_old_mode(): assert "@py_builtins" not in globals() """) - result = testdir.runpytest("--assertmode=reinterp") + result = testdir.runpytest("--assert=reinterp") assert result.ret == 0 def test_triple_quoted_string_issue113(testdir): @@ -246,11 +240,11 @@ p1 = testdir.makepyfile("") result = testdir.run(sys.executable, "-OO", "-m", "pytest", "-h") result.stderr.fnmatch_lines([ - "*WARNING*assertion*", + "*WARNING*assert statements are not executed*", ]) result = testdir.run(sys.executable, "-OO", "-m", "pytest", "--no-assert") result.stderr.fnmatch_lines([ - "*WARNING*assertion*", + "*WARNING*assert statements are not executed*", ]) def test_load_fake_pyc(testdir): Repository URL: https://bitbucket.org/hpk42/pytest/ -- 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 Tue Jul 5 18:01:47 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Tue, 05 Jul 2011 16:01:47 -0000 Subject: [py-svn] commit/pytest: hpk42: fix pytest-xdist breakage Message-ID: <20110705160147.19842.63072@bitbucket02.managed.contegix.com> 1 new changeset in pytest: http://bitbucket.org/hpk42/pytest/changeset/9ae391446a4c/ changeset: 9ae391446a4c user: hpk42 date: 2011-07-05 18:01:31 summary: fix pytest-xdist breakage affected #: 3 files (168 bytes) --- a/_pytest/__init__.py Tue Jul 05 17:29:53 2011 +0200 +++ b/_pytest/__init__.py Tue Jul 05 18:01:31 2011 +0200 @@ -1,2 +1,2 @@ # -__version__ = '2.1.0.dev6' +__version__ = '2.1.0.dev7' --- a/_pytest/assertion/__init__.py Tue Jul 05 17:29:53 2011 +0200 +++ b/_pytest/assertion/__init__.py Tue Jul 05 18:01:31 2011 +0200 @@ -68,7 +68,10 @@ if hook is not None: sys.meta_path.remove(hook) -def pytest_sessionstart(session): +def pytest_collection(session): + # this hook is only called when test modules are collected + # so for example not in the master process of pytest-xdist + # (which does not collect test modules) hook = session.config._assertstate.hook if hook is not None: hook.set_session(session) --- a/setup.py Tue Jul 05 17:29:53 2011 +0200 +++ b/setup.py Tue Jul 05 18:01:31 2011 +0200 @@ -22,7 +22,7 @@ name='pytest', description='py.test: simple powerful testing with Python', long_description = long_description, - version='2.1.0.dev6', + version='2.1.0.dev7', url='http://pytest.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], Repository URL: https://bitbucket.org/hpk42/pytest/ -- 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 Tue Jul 5 18:25:19 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Tue, 05 Jul 2011 16:25:19 -0000 Subject: [py-svn] commit/py: 2 new changesets Message-ID: <20110705162519.6529.45102@bitbucket01.managed.contegix.com> 2 new changesets in py: http://bitbucket.org/hpk42/py/changeset/54fda545ab91/ changeset: 54fda545ab91 user: gutworth date: 2011-07-05 18:13:28 summary: backout e2bc0b67063e affected #: 9 files (43.5 KB) --- a/CHANGELOG Sat Jun 18 19:30:54 2011 -0500 +++ b/CHANGELOG Tue Jul 05 11:13:28 2011 -0500 @@ -1,10 +1,3 @@ -Major changes -============= - -- Remove all code related to assertion introspection. This affects several - public APIs: py.code.patch_builtins/unpatch_builtin lost the assertion - argument. Traceback entries no longer have a reinterpret method. - Changes between 1.4.2 and 1.4.3 ================================================== --- a/py/__init__.py Sat Jun 18 19:30:54 2011 -0500 +++ b/py/__init__.py Tue Jul 05 11:13:28 2011 -0500 @@ -70,6 +70,11 @@ 'getrawcode' : '._code.code:getrawcode', 'patch_builtins' : '._code.code:patch_builtins', 'unpatch_builtins' : '._code.code:unpatch_builtins', + '_AssertionError' : '._code.assertion:AssertionError', + '_reinterpret_old' : '._code.assertion:reinterpret_old', + '_reinterpret' : '._code.assertion:reinterpret', + '_reprcompare' : '._code.assertion:_reprcompare', + '_format_explanation' : '._code.assertion:_format_explanation', }, # backports and additions of builtins --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/py/_code/_assertionnew.py Tue Jul 05 11:13:28 2011 -0500 @@ -0,0 +1,339 @@ +""" +Find intermediate evalutation results in assert statements through builtin AST. +This should replace _assertionold.py eventually. +""" + +import sys +import ast + +import py +from py._code.assertion import _format_explanation, BuiltinAssertionError + + +if sys.platform.startswith("java") and sys.version_info < (2, 5, 2): + # See http://bugs.jython.org/issue1497 + _exprs = ("BoolOp", "BinOp", "UnaryOp", "Lambda", "IfExp", "Dict", + "ListComp", "GeneratorExp", "Yield", "Compare", "Call", + "Repr", "Num", "Str", "Attribute", "Subscript", "Name", + "List", "Tuple") + _stmts = ("FunctionDef", "ClassDef", "Return", "Delete", "Assign", + "AugAssign", "Print", "For", "While", "If", "With", "Raise", + "TryExcept", "TryFinally", "Assert", "Import", "ImportFrom", + "Exec", "Global", "Expr", "Pass", "Break", "Continue") + _expr_nodes = set(getattr(ast, name) for name in _exprs) + _stmt_nodes = set(getattr(ast, name) for name in _stmts) + def _is_ast_expr(node): + return node.__class__ in _expr_nodes + def _is_ast_stmt(node): + return node.__class__ in _stmt_nodes +else: + def _is_ast_expr(node): + return isinstance(node, ast.expr) + def _is_ast_stmt(node): + return isinstance(node, ast.stmt) + + +class Failure(Exception): + """Error found while interpreting AST.""" + + def __init__(self, explanation=""): + self.cause = sys.exc_info() + self.explanation = explanation + + +def interpret(source, frame, should_fail=False): + mod = ast.parse(source) + visitor = DebugInterpreter(frame) + try: + visitor.visit(mod) + except Failure: + failure = sys.exc_info()[1] + return getfailure(failure) + if should_fail: + 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 --no-assert)") + +def run(offending_line, frame=None): + if frame is None: + frame = py.code.Frame(sys._getframe(1)) + return interpret(offending_line, frame) + +def getfailure(failure): + explanation = _format_explanation(failure.explanation) + value = failure.cause[1] + if str(value): + lines = explanation.splitlines() + if not lines: + lines.append("") + lines[0] += " << %s" % (value,) + explanation = "\n".join(lines) + text = "%s: %s" % (failure.cause[0].__name__, explanation) + if text.startswith("AssertionError: assert "): + text = text[16:] + return text + + +operator_map = { + ast.BitOr : "|", + ast.BitXor : "^", + ast.BitAnd : "&", + ast.LShift : "<<", + ast.RShift : ">>", + ast.Add : "+", + ast.Sub : "-", + ast.Mult : "*", + ast.Div : "/", + ast.FloorDiv : "//", + ast.Mod : "%", + ast.Eq : "==", + ast.NotEq : "!=", + ast.Lt : "<", + ast.LtE : "<=", + ast.Gt : ">", + ast.GtE : ">=", + ast.Pow : "**", + ast.Is : "is", + ast.IsNot : "is not", + ast.In : "in", + ast.NotIn : "not in" +} + +unary_map = { + ast.Not : "not %s", + ast.Invert : "~%s", + ast.USub : "-%s", + ast.UAdd : "+%s" +} + + +class DebugInterpreter(ast.NodeVisitor): + """Interpret AST nodes to gleam useful debugging information. """ + + def __init__(self, frame): + self.frame = frame + + def generic_visit(self, node): + # Fallback when we don't have a special implementation. + if _is_ast_expr(node): + mod = ast.Expression(node) + co = self._compile(mod) + try: + result = self.frame.eval(co) + except Exception: + raise Failure() + explanation = self.frame.repr(result) + return explanation, result + elif _is_ast_stmt(node): + mod = ast.Module([node]) + co = self._compile(mod, "exec") + try: + self.frame.exec_(co) + except Exception: + raise Failure() + return None, None + else: + raise AssertionError("can't handle %s" %(node,)) + + def _compile(self, source, mode="eval"): + return compile(source, "", mode) + + def visit_Expr(self, expr): + return self.visit(expr.value) + + def visit_Module(self, mod): + for stmt in mod.body: + self.visit(stmt) + + def visit_Name(self, name): + explanation, result = self.generic_visit(name) + # See if the name is local. + source = "%r in locals() is not globals()" % (name.id,) + co = self._compile(source) + try: + local = self.frame.eval(co) + except Exception: + # have to assume it isn't + local = False + if not local: + return name.id, result + return explanation, result + + def visit_Compare(self, comp): + left = comp.left + left_explanation, left_result = self.visit(left) + for op, next_op in zip(comp.ops, comp.comparators): + next_explanation, next_result = self.visit(next_op) + op_symbol = operator_map[op.__class__] + explanation = "%s %s %s" % (left_explanation, op_symbol, + next_explanation) + source = "__exprinfo_left %s __exprinfo_right" % (op_symbol,) + co = self._compile(source) + try: + result = self.frame.eval(co, __exprinfo_left=left_result, + __exprinfo_right=next_result) + except Exception: + raise Failure(explanation) + try: + if not result: + break + except KeyboardInterrupt: + raise + except: + break + left_explanation, left_result = next_explanation, next_result + + rcomp = py.code._reprcompare + if rcomp: + res = rcomp(op_symbol, left_result, next_result) + if res: + explanation = res + return explanation, result + + def visit_BoolOp(self, boolop): + is_or = isinstance(boolop.op, ast.Or) + explanations = [] + for operand in boolop.values: + explanation, result = self.visit(operand) + explanations.append(explanation) + if result == is_or: + break + name = is_or and " or " or " and " + explanation = "(" + name.join(explanations) + ")" + return explanation, result + + def visit_UnaryOp(self, unary): + pattern = unary_map[unary.op.__class__] + operand_explanation, operand_result = self.visit(unary.operand) + explanation = pattern % (operand_explanation,) + co = self._compile(pattern % ("__exprinfo_expr",)) + try: + result = self.frame.eval(co, __exprinfo_expr=operand_result) + except Exception: + raise Failure(explanation) + return explanation, result + + def visit_BinOp(self, binop): + left_explanation, left_result = self.visit(binop.left) + right_explanation, right_result = self.visit(binop.right) + symbol = operator_map[binop.op.__class__] + explanation = "(%s %s %s)" % (left_explanation, symbol, + right_explanation) + source = "__exprinfo_left %s __exprinfo_right" % (symbol,) + co = self._compile(source) + try: + result = self.frame.eval(co, __exprinfo_left=left_result, + __exprinfo_right=right_result) + except Exception: + raise Failure(explanation) + return explanation, result + + def visit_Call(self, call): + func_explanation, func = self.visit(call.func) + arg_explanations = [] + ns = {"__exprinfo_func" : func} + arguments = [] + for arg in call.args: + arg_explanation, arg_result = self.visit(arg) + arg_name = "__exprinfo_%s" % (len(ns),) + ns[arg_name] = arg_result + arguments.append(arg_name) + arg_explanations.append(arg_explanation) + for keyword in call.keywords: + arg_explanation, arg_result = self.visit(keyword.value) + arg_name = "__exprinfo_%s" % (len(ns),) + ns[arg_name] = arg_result + keyword_source = "%s=%%s" % (keyword.arg) + arguments.append(keyword_source % (arg_name,)) + arg_explanations.append(keyword_source % (arg_explanation,)) + if call.starargs: + arg_explanation, arg_result = self.visit(call.starargs) + arg_name = "__exprinfo_star" + ns[arg_name] = arg_result + arguments.append("*%s" % (arg_name,)) + arg_explanations.append("*%s" % (arg_explanation,)) + if call.kwargs: + arg_explanation, arg_result = self.visit(call.kwargs) + arg_name = "__exprinfo_kwds" + ns[arg_name] = arg_result + arguments.append("**%s" % (arg_name,)) + arg_explanations.append("**%s" % (arg_explanation,)) + args_explained = ", ".join(arg_explanations) + explanation = "%s(%s)" % (func_explanation, args_explained) + args = ", ".join(arguments) + source = "__exprinfo_func(%s)" % (args,) + co = self._compile(source) + try: + result = self.frame.eval(co, **ns) + except Exception: + raise Failure(explanation) + pattern = "%s\n{%s = %s\n}" + rep = self.frame.repr(result) + explanation = pattern % (rep, rep, explanation) + return explanation, result + + def _is_builtin_name(self, name): + pattern = "%r not in globals() and %r not in locals()" + source = pattern % (name.id, name.id) + co = self._compile(source) + try: + return self.frame.eval(co) + except Exception: + return False + + def visit_Attribute(self, attr): + if not isinstance(attr.ctx, ast.Load): + return self.generic_visit(attr) + source_explanation, source_result = self.visit(attr.value) + explanation = "%s.%s" % (source_explanation, attr.attr) + source = "__exprinfo_expr.%s" % (attr.attr,) + co = self._compile(source) + try: + result = self.frame.eval(co, __exprinfo_expr=source_result) + except Exception: + raise Failure(explanation) + explanation = "%s\n{%s = %s.%s\n}" % (self.frame.repr(result), + self.frame.repr(result), + source_explanation, attr.attr) + # Check if the attr is from an instance. + source = "%r in getattr(__exprinfo_expr, '__dict__', {})" + source = source % (attr.attr,) + co = self._compile(source) + try: + from_instance = self.frame.eval(co, __exprinfo_expr=source_result) + except Exception: + from_instance = True + if from_instance: + rep = self.frame.repr(result) + pattern = "%s\n{%s = %s\n}" + explanation = pattern % (rep, rep, explanation) + return explanation, result + + def visit_Assert(self, assrt): + test_explanation, test_result = self.visit(assrt.test) + if test_explanation.startswith("False\n{False =") and \ + test_explanation.endswith("\n"): + test_explanation = test_explanation[15:-2] + explanation = "assert %s" % (test_explanation,) + if not test_result: + try: + raise BuiltinAssertionError + except Exception: + raise Failure(explanation) + return explanation, test_result + + def visit_Assign(self, assign): + value_explanation, value_result = self.visit(assign.value) + explanation = "... = %s" % (value_explanation,) + name = ast.Name("__exprinfo_expr", ast.Load(), + lineno=assign.value.lineno, + col_offset=assign.value.col_offset) + new_assign = ast.Assign(assign.targets, name, lineno=assign.lineno, + col_offset=assign.col_offset) + mod = ast.Module([new_assign]) + co = self._compile(mod, "exec") + try: + self.frame.exec_(co, __exprinfo_expr=value_result) + except Exception: + raise Failure(explanation) + return explanation, value_result --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/py/_code/_assertionold.py Tue Jul 05 11:13:28 2011 -0500 @@ -0,0 +1,555 @@ +import py +import sys, inspect +from compiler import parse, ast, pycodegen +from py._code.assertion import BuiltinAssertionError, _format_explanation + +passthroughex = py.builtin._sysex + +class Failure: + def __init__(self, node): + self.exc, self.value, self.tb = sys.exc_info() + self.node = node + +class View(object): + """View base class. + + If C is a subclass of View, then C(x) creates a proxy object around + the object x. The actual class of the proxy is not C in general, + but a *subclass* of C determined by the rules below. To avoid confusion + we call view class the class of the proxy (a subclass of C, so of View) + and object class the class of x. + + Attributes and methods not found in the proxy are automatically read on x. + Other operations like setting attributes are performed on the proxy, as + determined by its view class. The object x is available from the proxy + as its __obj__ attribute. + + The view class selection is determined by the __view__ tuples and the + optional __viewkey__ method. By default, the selected view class is the + most specific subclass of C whose __view__ mentions the class of x. + If no such subclass is found, the search proceeds with the parent + object classes. For example, C(True) will first look for a subclass + of C with __view__ = (..., bool, ...) and only if it doesn't find any + look for one with __view__ = (..., int, ...), and then ..., object,... + If everything fails the class C itself is considered to be the default. + + Alternatively, the view class selection can be driven by another aspect + of the object x, instead of the class of x, by overriding __viewkey__. + See last example at the end of this module. + """ + + _viewcache = {} + __view__ = () + + def __new__(rootclass, obj, *args, **kwds): + self = object.__new__(rootclass) + self.__obj__ = obj + self.__rootclass__ = rootclass + key = self.__viewkey__() + try: + self.__class__ = self._viewcache[key] + except KeyError: + self.__class__ = self._selectsubclass(key) + return self + + def __getattr__(self, attr): + # attributes not found in the normal hierarchy rooted on View + # are looked up in the object's real class + return getattr(self.__obj__, attr) + + def __viewkey__(self): + return self.__obj__.__class__ + + def __matchkey__(self, key, subclasses): + if inspect.isclass(key): + keys = inspect.getmro(key) + else: + keys = [key] + for key in keys: + result = [C for C in subclasses if key in C.__view__] + if result: + return result + return [] + + def _selectsubclass(self, key): + subclasses = list(enumsubclasses(self.__rootclass__)) + for C in subclasses: + if not isinstance(C.__view__, tuple): + C.__view__ = (C.__view__,) + choices = self.__matchkey__(key, subclasses) + if not choices: + return self.__rootclass__ + elif len(choices) == 1: + return choices[0] + else: + # combine the multiple choices + return type('?', tuple(choices), {}) + + def __repr__(self): + return '%s(%r)' % (self.__rootclass__.__name__, self.__obj__) + + +def enumsubclasses(cls): + for subcls in cls.__subclasses__(): + for subsubclass in enumsubclasses(subcls): + yield subsubclass + yield cls + + +class Interpretable(View): + """A parse tree node with a few extra methods.""" + explanation = None + + def is_builtin(self, frame): + return False + + def eval(self, frame): + # fall-back for unknown expression nodes + try: + expr = ast.Expression(self.__obj__) + expr.filename = '' + self.__obj__.filename = '' + co = pycodegen.ExpressionCodeGenerator(expr).getCode() + result = frame.eval(co) + except passthroughex: + raise + except: + raise Failure(self) + self.result = result + self.explanation = self.explanation or frame.repr(self.result) + + def run(self, frame): + # fall-back for unknown statement nodes + try: + expr = ast.Module(None, ast.Stmt([self.__obj__])) + expr.filename = '' + co = pycodegen.ModuleCodeGenerator(expr).getCode() + frame.exec_(co) + except passthroughex: + raise + except: + raise Failure(self) + + def nice_explanation(self): + return _format_explanation(self.explanation) + + +class Name(Interpretable): + __view__ = ast.Name + + def is_local(self, frame): + source = '%r in locals() is not globals()' % self.name + try: + return frame.is_true(frame.eval(source)) + except passthroughex: + raise + except: + return False + + def is_global(self, frame): + source = '%r in globals()' % self.name + try: + return frame.is_true(frame.eval(source)) + except passthroughex: + raise + except: + return False + + def is_builtin(self, frame): + source = '%r not in locals() and %r not in globals()' % ( + self.name, self.name) + try: + return frame.is_true(frame.eval(source)) + except passthroughex: + raise + except: + return False + + def eval(self, frame): + super(Name, self).eval(frame) + if not self.is_local(frame): + self.explanation = self.name + +class Compare(Interpretable): + __view__ = ast.Compare + + def eval(self, frame): + expr = Interpretable(self.expr) + expr.eval(frame) + for operation, expr2 in self.ops: + if hasattr(self, 'result'): + # shortcutting in chained expressions + if not frame.is_true(self.result): + break + expr2 = Interpretable(expr2) + expr2.eval(frame) + self.explanation = "%s %s %s" % ( + expr.explanation, operation, expr2.explanation) + source = "__exprinfo_left %s __exprinfo_right" % operation + try: + self.result = frame.eval(source, + __exprinfo_left=expr.result, + __exprinfo_right=expr2.result) + except passthroughex: + raise + except: + raise Failure(self) + expr = expr2 + +class And(Interpretable): + __view__ = ast.And + + def eval(self, frame): + explanations = [] + for expr in self.nodes: + expr = Interpretable(expr) + expr.eval(frame) + explanations.append(expr.explanation) + self.result = expr.result + if not frame.is_true(expr.result): + break + self.explanation = '(' + ' and '.join(explanations) + ')' + +class Or(Interpretable): + __view__ = ast.Or + + def eval(self, frame): + explanations = [] + for expr in self.nodes: + expr = Interpretable(expr) + expr.eval(frame) + explanations.append(expr.explanation) + self.result = expr.result + if frame.is_true(expr.result): + break + self.explanation = '(' + ' or '.join(explanations) + ')' + + +# == Unary operations == +keepalive = [] +for astclass, astpattern in { + ast.Not : 'not __exprinfo_expr', + ast.Invert : '(~__exprinfo_expr)', + }.items(): + + class UnaryArith(Interpretable): + __view__ = astclass + + def eval(self, frame, astpattern=astpattern): + expr = Interpretable(self.expr) + expr.eval(frame) + self.explanation = astpattern.replace('__exprinfo_expr', + expr.explanation) + try: + self.result = frame.eval(astpattern, + __exprinfo_expr=expr.result) + except passthroughex: + raise + except: + raise Failure(self) + + keepalive.append(UnaryArith) + +# == Binary operations == +for astclass, astpattern in { + ast.Add : '(__exprinfo_left + __exprinfo_right)', + ast.Sub : '(__exprinfo_left - __exprinfo_right)', + ast.Mul : '(__exprinfo_left * __exprinfo_right)', + ast.Div : '(__exprinfo_left / __exprinfo_right)', + ast.Mod : '(__exprinfo_left % __exprinfo_right)', + ast.Power : '(__exprinfo_left ** __exprinfo_right)', + }.items(): + + class BinaryArith(Interpretable): + __view__ = astclass + + def eval(self, frame, astpattern=astpattern): + left = Interpretable(self.left) + left.eval(frame) + right = Interpretable(self.right) + right.eval(frame) + self.explanation = (astpattern + .replace('__exprinfo_left', left .explanation) + .replace('__exprinfo_right', right.explanation)) + try: + self.result = frame.eval(astpattern, + __exprinfo_left=left.result, + __exprinfo_right=right.result) + except passthroughex: + raise + except: + raise Failure(self) + + keepalive.append(BinaryArith) + + +class CallFunc(Interpretable): + __view__ = ast.CallFunc + + def is_bool(self, frame): + source = 'isinstance(__exprinfo_value, bool)' + try: + return frame.is_true(frame.eval(source, + __exprinfo_value=self.result)) + except passthroughex: + raise + except: + return False + + def eval(self, frame): + node = Interpretable(self.node) + node.eval(frame) + explanations = [] + vars = {'__exprinfo_fn': node.result} + source = '__exprinfo_fn(' + for a in self.args: + if isinstance(a, ast.Keyword): + keyword = a.name + a = a.expr + else: + keyword = None + a = Interpretable(a) + a.eval(frame) + argname = '__exprinfo_%d' % len(vars) + vars[argname] = a.result + if keyword is None: + source += argname + ',' + explanations.append(a.explanation) + else: + source += '%s=%s,' % (keyword, argname) + explanations.append('%s=%s' % (keyword, a.explanation)) + if self.star_args: + star_args = Interpretable(self.star_args) + star_args.eval(frame) + argname = '__exprinfo_star' + vars[argname] = star_args.result + source += '*' + argname + ',' + explanations.append('*' + star_args.explanation) + if self.dstar_args: + dstar_args = Interpretable(self.dstar_args) + dstar_args.eval(frame) + argname = '__exprinfo_kwds' + vars[argname] = dstar_args.result + source += '**' + argname + ',' + explanations.append('**' + dstar_args.explanation) + self.explanation = "%s(%s)" % ( + node.explanation, ', '.join(explanations)) + if source.endswith(','): + source = source[:-1] + source += ')' + try: + self.result = frame.eval(source, **vars) + except passthroughex: + raise + except: + raise Failure(self) + if not node.is_builtin(frame) or not self.is_bool(frame): + r = frame.repr(self.result) + self.explanation = '%s\n{%s = %s\n}' % (r, r, self.explanation) + +class Getattr(Interpretable): + __view__ = ast.Getattr + + def eval(self, frame): + expr = Interpretable(self.expr) + expr.eval(frame) + source = '__exprinfo_expr.%s' % self.attrname + try: + self.result = frame.eval(source, __exprinfo_expr=expr.result) + except passthroughex: + raise + except: + raise Failure(self) + self.explanation = '%s.%s' % (expr.explanation, self.attrname) + # if the attribute comes from the instance, its value is interesting + source = ('hasattr(__exprinfo_expr, "__dict__") and ' + '%r in __exprinfo_expr.__dict__' % self.attrname) + try: + from_instance = frame.is_true( + frame.eval(source, __exprinfo_expr=expr.result)) + except passthroughex: + raise + except: + from_instance = True + if from_instance: + r = frame.repr(self.result) + self.explanation = '%s\n{%s = %s\n}' % (r, r, self.explanation) + +# == Re-interpretation of full statements == + +class Assert(Interpretable): + __view__ = ast.Assert + + def run(self, frame): + test = Interpretable(self.test) + test.eval(frame) + # simplify 'assert False where False = ...' + if (test.explanation.startswith('False\n{False = ') and + test.explanation.endswith('\n}')): + test.explanation = test.explanation[15:-2] + # print the result as 'assert ' + self.result = test.result + self.explanation = 'assert ' + test.explanation + if not frame.is_true(test.result): + try: + raise BuiltinAssertionError + except passthroughex: + raise + except: + raise Failure(self) + +class Assign(Interpretable): + __view__ = ast.Assign + + def run(self, frame): + expr = Interpretable(self.expr) + expr.eval(frame) + self.result = expr.result + self.explanation = '... = ' + expr.explanation + # fall-back-run the rest of the assignment + ass = ast.Assign(self.nodes, ast.Name('__exprinfo_expr')) + mod = ast.Module(None, ast.Stmt([ass])) + mod.filename = '' + co = pycodegen.ModuleCodeGenerator(mod).getCode() + try: + frame.exec_(co, __exprinfo_expr=expr.result) + except passthroughex: + raise + except: + raise Failure(self) + +class Discard(Interpretable): + __view__ = ast.Discard + + def run(self, frame): + expr = Interpretable(self.expr) + expr.eval(frame) + self.result = expr.result + self.explanation = expr.explanation + +class Stmt(Interpretable): + __view__ = ast.Stmt + + def run(self, frame): + for stmt in self.nodes: + stmt = Interpretable(stmt) + stmt.run(frame) + + +def report_failure(e): + explanation = e.node.nice_explanation() + if explanation: + explanation = ", in: " + explanation + else: + explanation = "" + sys.stdout.write("%s: %s%s\n" % (e.exc.__name__, e.value, explanation)) + +def check(s, frame=None): + if frame is None: + frame = sys._getframe(1) + frame = py.code.Frame(frame) + expr = parse(s, 'eval') + assert isinstance(expr, ast.Expression) + node = Interpretable(expr.node) + try: + node.eval(frame) + except passthroughex: + raise + except Failure: + e = sys.exc_info()[1] + report_failure(e) + else: + if not frame.is_true(node.result): + sys.stderr.write("assertion failed: %s\n" % node.nice_explanation()) + + +########################################################### +# API / Entry points +# ######################################################### + +def interpret(source, frame, should_fail=False): + module = Interpretable(parse(source, 'exec').node) + #print "got module", module + if isinstance(frame, py.std.types.FrameType): + frame = py.code.Frame(frame) + try: + module.run(frame) + except Failure: + e = sys.exc_info()[1] + return getfailure(e) + except passthroughex: + raise + except: + import traceback + traceback.print_exc() + if should_fail: + 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 + +def getmsg(excinfo): + if isinstance(excinfo, tuple): + excinfo = py.code.ExceptionInfo(excinfo) + #frame, line = gettbline(tb) + #frame = py.code.Frame(frame) + #return interpret(line, frame) + + tb = excinfo.traceback[-1] + source = str(tb.statement).strip() + x = interpret(source, tb.frame, should_fail=True) + if not isinstance(x, str): + raise TypeError("interpret returned non-string %r" % (x,)) + return x + +def getfailure(e): + explanation = e.node.nice_explanation() + if str(e.value): + lines = explanation.split('\n') + lines[0] += " << %s" % (e.value,) + explanation = '\n'.join(lines) + text = "%s: %s" % (e.exc.__name__, explanation) + if text.startswith('AssertionError: assert '): + text = text[16:] + return text + +def run(s, frame=None): + if frame is None: + frame = sys._getframe(1) + frame = py.code.Frame(frame) + module = Interpretable(parse(s, 'exec').node) + try: + module.run(frame) + except Failure: + e = sys.exc_info()[1] + report_failure(e) + + +if __name__ == '__main__': + # example: + def f(): + return 5 + def g(): + return 3 + def h(x): + return 'never' + check("f() * g() == 5") + check("not f()") + check("not (f() and g() or 0)") + check("f() == g()") + i = 4 + check("i == f()") + check("len(f()) == 0") + check("isinstance(2+3+4, float)") + + run("x = i") + check("x == 5") + + run("assert not f(), 'oops'") + run("a, b, c = 1, 2") + run("a, b, c = f()") + + check("max([f(),g()]) == 4") + check("'hello'[g()] == 'h'") + run("'guk%d' % h(f())") --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/py/_code/assertion.py Tue Jul 05 11:13:28 2011 -0500 @@ -0,0 +1,94 @@ +import sys +import py + +BuiltinAssertionError = py.builtin.builtins.AssertionError + +_reprcompare = None # if set, will be called by assert reinterp for comparison ops + +def _format_explanation(explanation): + """This formats an explanation + + Normally all embedded newlines are escaped, however there are + three exceptions: \n{, \n} and \n~. The first two are intended + cover nested explanations, see function and attribute explanations + for examples (.visit_Call(), visit_Attribute()). The last one is + for when one explanation needs to span multiple lines, e.g. when + displaying diffs. + """ + raw_lines = (explanation or '').split('\n') + # escape newlines not followed by {, } and ~ + lines = [raw_lines[0]] + for l in raw_lines[1:]: + if l.startswith('{') or l.startswith('}') or l.startswith('~'): + lines.append(l) + else: + lines[-1] += '\\n' + l + + result = lines[:1] + stack = [0] + stackcnt = [0] + for line in lines[1:]: + if line.startswith('{'): + if stackcnt[-1]: + s = 'and ' + else: + s = 'where ' + stack.append(len(result)) + stackcnt[-1] += 1 + stackcnt.append(0) + result.append(' +' + ' '*(len(stack)-1) + s + line[1:]) + elif line.startswith('}'): + assert line.startswith('}') + stack.pop() + stackcnt.pop() + result[stack[-1]] += line[1:] + else: + assert line.startswith('~') + result.append(' '*len(stack) + line[1:]) + assert len(stack) == 1 + return '\n'.join(result) + + +class AssertionError(BuiltinAssertionError): + def __init__(self, *args): + BuiltinAssertionError.__init__(self, *args) + if args: + try: + self.msg = str(args[0]) + except py.builtin._sysex: + raise + except: + self.msg = "<[broken __repr__] %s at %0xd>" %( + args[0].__class__, id(args[0])) + else: + f = py.code.Frame(sys._getframe(1)) + try: + source = f.code.fullsource + if source is not None: + try: + source = source.getstatement(f.lineno, assertion=True) + except IndexError: + source = None + else: + source = str(source.deindent()).strip() + except py.error.ENOENT: + source = None + # this can also occur during reinterpretation, when the + # co_filename is set to "". + if source: + self.msg = reinterpret(source, f, should_fail=True) + else: + self.msg = "" + if not self.args: + self.args = (self.msg,) + +if sys.version_info > (3, 0): + AssertionError.__module__ = "builtins" + reinterpret_old = "old reinterpretation not available for py3" +else: + from py._code._assertionold import interpret as reinterpret_old +if sys.version_info >= (2, 6) or (sys.platform.startswith("java")): + from py._code._assertionnew import interpret as reinterpret +else: + reinterpret = reinterpret_old + --- a/py/_code/code.py Sat Jun 18 19:30:54 2011 -0500 +++ b/py/_code/code.py Tue Jul 05 11:13:28 2011 -0500 @@ -145,6 +145,17 @@ return self.frame.f_locals locals = property(getlocals, None, None, "locals of underlaying frame") + def reinterpret(self): + """Reinterpret the failing statement and returns a detailed information + about what operations are performed.""" + if self.exprinfo is None: + source = str(self.statement).strip() + x = py.code._reinterpret(source, self.frame, should_fail=True) + if not isinstance(x, str): + raise TypeError("interpret returned non-string %r" % (x,)) + self.exprinfo = x + return self.exprinfo + def getfirstlinesource(self): # on Jython this firstlineno can be -1 apparently return max(self.frame.code.firstlineno, 0) @@ -679,15 +690,22 @@ oldbuiltins = {} -def patch_builtins(compile=True): - """ put compile builtins to Python's builtins. """ +def patch_builtins(assertion=True, compile=True): + """ put compile and AssertionError builtins to Python's builtins. """ + if assertion: + from py._code import assertion + l = oldbuiltins.setdefault('AssertionError', []) + l.append(py.builtin.builtins.AssertionError) + py.builtin.builtins.AssertionError = assertion.AssertionError if compile: l = oldbuiltins.setdefault('compile', []) l.append(py.builtin.builtins.compile) py.builtin.builtins.compile = py.code.compile -def unpatch_builtins(compile=True): +def unpatch_builtins(assertion=True, compile=True): """ remove compile and AssertionError builtins from Python builtins. """ + if assertion: + py.builtin.builtins.AssertionError = oldbuiltins['AssertionError'].pop() if compile: py.builtin.builtins.compile = oldbuiltins['compile'].pop() --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/testing/code/test_assertion.py Tue Jul 05 11:13:28 2011 -0500 @@ -0,0 +1,331 @@ +import pytest, py + +def exvalue(): + return py.std.sys.exc_info()[1] + +def setup_module(mod): + py.code.patch_builtins(assertion=True, compile=False) + +def teardown_module(mod): + py.code.unpatch_builtins(assertion=True, compile=False) + +def f(): + return 2 + +def test_assert(): + try: + assert f() == 3 + except AssertionError: + e = exvalue() + s = str(e) + assert s.startswith('assert 2 == 3\n') + +def test_assert_with_explicit_message(): + try: + assert f() == 3, "hello" + except AssertionError: + e = exvalue() + assert e.msg == 'hello' + +def test_assert_within_finally(): + class A: + def f(): + pass + excinfo = py.test.raises(TypeError, """ + try: + A().f() + finally: + i = 42 + """) + s = excinfo.exconly() + assert s.find("takes no argument") != -1 + + #def g(): + # A.f() + #excinfo = getexcinfo(TypeError, g) + #msg = getmsg(excinfo) + #assert msg.find("must be called with A") != -1 + + +def test_assert_multiline_1(): + try: + assert (f() == + 3) + except AssertionError: + e = exvalue() + s = str(e) + assert s.startswith('assert 2 == 3\n') + +def test_assert_multiline_2(): + try: + assert (f() == (4, + 3)[-1]) + except AssertionError: + e = exvalue() + s = str(e) + assert s.startswith('assert 2 ==') + +def test_in(): + try: + assert "hi" in [1, 2] + except AssertionError: + e = exvalue() + s = str(e) + assert s.startswith("assert 'hi' in") + +def test_is(): + try: + assert 1 is 2 + except AssertionError: + e = exvalue() + s = str(e) + assert s.startswith("assert 1 is 2") + + + at py.test.mark.skipif("sys.version_info < (2,6)") +def test_attrib(): + class Foo(object): + b = 1 + i = Foo() + try: + assert i.b == 2 + except AssertionError: + e = exvalue() + s = str(e) + assert s.startswith("assert 1 == 2") + + at py.test.mark.skipif("sys.version_info < (2,6)") +def test_attrib_inst(): + class Foo(object): + b = 1 + try: + assert Foo().b == 2 + except AssertionError: + e = exvalue() + s = str(e) + assert s.startswith("assert 1 == 2") + +def test_len(): + l = list(range(42)) + try: + assert len(l) == 100 + except AssertionError: + e = exvalue() + s = str(e) + assert s.startswith("assert 42 == 100") + assert "where 42 = len([" in s + +def test_assert_non_string_message(): + class A: + def __str__(self): + return "hello" + try: + assert 0 == 1, A() + except AssertionError: + e = exvalue() + assert e.msg == "hello" + +def test_assert_keyword_arg(): + def f(x=3): + return False + try: + assert f(x=5) + except AssertionError: + e = exvalue() + assert "x=5" in e.msg + +# These tests should both fail, but should fail nicely... +class WeirdRepr: + def __repr__(self): + return '' + +def bug_test_assert_repr(): + v = WeirdRepr() + try: + assert v == 1 + except AssertionError: + e = exvalue() + assert e.msg.find('WeirdRepr') != -1 + assert e.msg.find('second line') != -1 + assert 0 + +def test_assert_non_string(): + try: + assert 0, ['list'] + except AssertionError: + e = exvalue() + assert e.msg.find("list") != -1 + +def test_assert_implicit_multiline(): + try: + x = [1,2,3] + assert x != [1, + 2, 3] + except AssertionError: + e = exvalue() + assert e.msg.find('assert [1, 2, 3] !=') != -1 + + +def test_assert_with_brokenrepr_arg(): + class BrokenRepr: + def __repr__(self): 0 / 0 + e = AssertionError(BrokenRepr()) + if e.msg.find("broken __repr__") == -1: + py.test.fail("broken __repr__ not handle correctly") + +def test_multiple_statements_per_line(): + try: + a = 1; assert a == 2 + except AssertionError: + e = exvalue() + assert "assert 1 == 2" in e.msg + +def test_power(): + try: + assert 2**3 == 7 + except AssertionError: + e = exvalue() + assert "assert (2 ** 3) == 7" in e.msg + + +class TestView: + + def setup_class(cls): + cls.View = py.test.importorskip("py._code._assertionold").View + + def test_class_dispatch(self): + ### Use a custom class hierarchy with existing instances + + class Picklable(self.View): + pass + + class Simple(Picklable): + __view__ = object + def pickle(self): + return repr(self.__obj__) + + class Seq(Picklable): + __view__ = list, tuple, dict + def pickle(self): + return ';'.join( + [Picklable(item).pickle() for item in self.__obj__]) + + class Dict(Seq): + __view__ = dict + def pickle(self): + return Seq.pickle(self) + '!' + Seq(self.values()).pickle() + + assert Picklable(123).pickle() == '123' + assert Picklable([1,[2,3],4]).pickle() == '1;2;3;4' + assert Picklable({1:2}).pickle() == '1!2' + + def test_viewtype_class_hierarchy(self): + # Use a custom class hierarchy based on attributes of existing instances + class Operation: + "Existing class that I don't want to change." + def __init__(self, opname, *args): + self.opname = opname + self.args = args + + existing = [Operation('+', 4, 5), + Operation('getitem', '', 'join'), + Operation('setattr', 'x', 'y', 3), + Operation('-', 12, 1)] + + class PyOp(self.View): + def __viewkey__(self): + return self.opname + def generate(self): + return '%s(%s)' % (self.opname, ', '.join(map(repr, self.args))) + + class PyBinaryOp(PyOp): + __view__ = ('+', '-', '*', '/') + def generate(self): + return '%s %s %s' % (self.args[0], self.opname, self.args[1]) + + codelines = [PyOp(op).generate() for op in existing] + assert codelines == ["4 + 5", "getitem('', 'join')", + "setattr('x', 'y', 3)", "12 - 1"] + +def test_underscore_api(): + py.code._AssertionError + py.code._reinterpret_old # used by pypy + py.code._reinterpret + + at py.test.mark.skipif("sys.version_info < (2,6)") +def test_assert_customizable_reprcompare(monkeypatch): + monkeypatch.setattr(py.code, '_reprcompare', lambda *args: 'hello') + try: + assert 3 == 4 + except AssertionError: + e = exvalue() + s = str(e) + assert "hello" in s + +def test_assert_long_source_1(): + try: + assert len == [ + (None, ['somet text', 'more text']), + ] + except AssertionError: + e = exvalue() + s = str(e) + assert 're-run' not in s + assert 'somet text' in s + +def test_assert_long_source_2(): + try: + assert(len == [ + (None, ['somet text', 'more text']), + ]) + except AssertionError: + e = exvalue() + s = str(e) + assert 're-run' not in s + assert 'somet text' in s + +def test_assert_raise_alias(testdir): + testdir.makepyfile(""" + import sys + EX = AssertionError + def test_hello(): + raise EX("hello" + "multi" + "line") + """) + result = testdir.runpytest() + result.stdout.fnmatch_lines([ + "*def test_hello*", + "*raise EX*", + "*1 failed*", + ]) + + + at pytest.mark.skipif("sys.version_info < (2,5)") +def test_assert_raise_subclass(): + class SomeEx(AssertionError): + def __init__(self, *args): + super(SomeEx, self).__init__() + try: + raise SomeEx("hello") + except AssertionError: + s = str(exvalue()) + assert 're-run' not in s + assert 'could not determine' in s + +def test_assert_raises_in_nonzero_of_object_pytest_issue10(): + class A(object): + def __nonzero__(self): + raise ValueError(42) + def __lt__(self, other): + return A() + def __repr__(self): + return "" + def myany(x): + return True + try: + assert not(myany(A() < 0)) + except AssertionError: + e = exvalue() + s = str(e) + assert "< 0" in s --- a/testing/code/test_code.py Sat Jun 18 19:30:54 2011 -0500 +++ b/testing/code/test_code.py Tue Jul 05 11:13:28 2011 -0500 @@ -68,10 +68,15 @@ comp = cpy_builtin.compile def mycompile(*args, **kwargs): return comp(*args, **kwargs) + class Sub(AssertionError): + pass + monkeypatch.setattr(cpy_builtin, 'AssertionError', Sub) monkeypatch.setattr(cpy_builtin, 'compile', mycompile) py.code.patch_builtins() + assert cpy_builtin.AssertionError != Sub assert cpy_builtin.compile != mycompile py.code.unpatch_builtins() + assert cpy_builtin.AssertionError is Sub assert cpy_builtin.compile == mycompile --- a/testing/code/test_excinfo.py Sat Jun 18 19:30:54 2011 -0500 +++ b/testing/code/test_excinfo.py Tue Jul 05 11:13:28 2011 -0500 @@ -193,6 +193,15 @@ def hello(x): x + 5 +def test_tbentry_reinterpret(): + try: + hello("hello") + except TypeError: + excinfo = py.code.ExceptionInfo() + tbentry = excinfo.traceback[-1] + msg = tbentry.reinterpret() + assert msg.startswith("TypeError: ('hello' + 5)") + def test_excinfo_exconly(): excinfo = py.test.raises(ValueError, h) assert excinfo.exconly().startswith('ValueError') @@ -586,6 +595,27 @@ assert reprtb.extraline == "!!! Recursion detected (same locals & position)" assert str(reprtb) + def test_tb_entry_AssertionError(self, importasmod): + # probably this test is a bit redundant + # as py/magic/testing/test_assertion.py + # already tests correctness of + # assertion-reinterpretation logic + mod = importasmod(""" + def somefunc(): + x = 1 + assert x == 2 + """) + py.code.patch_builtins(assertion=True) + try: + excinfo = py.test.raises(AssertionError, mod.somefunc) + finally: + py.code.unpatch_builtins(assertion=True) + + p = FormattedExcinfo() + reprentry = p.repr_traceback_entry(excinfo.traceback[-1], excinfo) + lines = reprentry.lines + assert lines[-1] == "E assert 1 == 2" + def test_reprexcinfo_getrepr(self, importasmod): mod = importasmod(""" def f(x): http://bitbucket.org/hpk42/py/changeset/ee2fd8c390bb/ changeset: ee2fd8c390bb user: gutworth date: 2011-07-05 18:29:33 summary: some fiddling to get py.test tests to pass affected #: 2 files (364 bytes) --- a/testing/code/test_assertion.py Tue Jul 05 11:13:28 2011 -0500 +++ b/testing/code/test_assertion.py Tue Jul 05 11:29:33 2011 -0500 @@ -3,12 +3,6 @@ def exvalue(): return py.std.sys.exc_info()[1] -def setup_module(mod): - py.code.patch_builtins(assertion=True, compile=False) - -def teardown_module(mod): - py.code.unpatch_builtins(assertion=True, compile=False) - def f(): return 2 @@ -254,7 +248,8 @@ @py.test.mark.skipif("sys.version_info < (2,6)") def test_assert_customizable_reprcompare(monkeypatch): - monkeypatch.setattr(py.code, '_reprcompare', lambda *args: 'hello') + import _pytest.assertion.util + monkeypatch.setattr(_pytest.assertion.util, '_reprcompare', lambda *args: 'hello') try: assert 3 == 4 except AssertionError: --- a/testing/code/test_excinfo.py Tue Jul 05 11:13:28 2011 -0500 +++ b/testing/code/test_excinfo.py Tue Jul 05 11:29:33 2011 -0500 @@ -296,15 +296,11 @@ def test_repr_source_excinfo(self): """ check if indentation is right """ pr = FormattedExcinfo() - py.code.patch_builtins() - try: - excinfo = self.excinfo_from_exec(""" + excinfo = self.excinfo_from_exec(""" def f(): assert 0 f() - """) - finally: - py.code.unpatch_builtins() + """) pr = FormattedExcinfo() source = pr._getentrysource(excinfo.traceback[-1]) lines = pr.get_source(source, 1, excinfo) @@ -605,11 +601,7 @@ x = 1 assert x == 2 """) - py.code.patch_builtins(assertion=True) - try: - excinfo = py.test.raises(AssertionError, mod.somefunc) - finally: - py.code.unpatch_builtins(assertion=True) + excinfo = py.test.raises(AssertionError, mod.somefunc) p = FormattedExcinfo() reprentry = p.repr_traceback_entry(excinfo.traceback[-1], excinfo) Repository URL: https://bitbucket.org/hpk42/py/ -- 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 Tue Jul 5 18:31:18 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Tue, 05 Jul 2011 16:31:18 -0000 Subject: [py-svn] commit/py: gutworth: don't show locals which start with @ Message-ID: <20110705163118.6529.81157@bitbucket01.managed.contegix.com> 1 new changeset in py: http://bitbucket.org/hpk42/py/changeset/91d2234a6c41/ changeset: 91d2234a6c41 user: gutworth date: 2011-07-05 18:35:32 summary: don't show locals which start with @ affected #: 2 files (35 bytes) --- a/py/_code/code.py Tue Jul 05 11:29:33 2011 -0500 +++ b/py/_code/code.py Tue Jul 05 11:35:32 2011 -0500 @@ -462,7 +462,7 @@ def repr_locals(self, locals): if self.showlocals: lines = [] - keys = list(locals) + keys = [loc for loc in locals if loc[0] != "@"] keys.sort() for name in keys: value = locals[name] --- a/testing/code/test_excinfo.py Tue Jul 05 11:29:33 2011 -0500 +++ b/testing/code/test_excinfo.py Tue Jul 05 11:35:32 2011 -0500 @@ -384,7 +384,7 @@ def test_repr_local(self): p = FormattedExcinfo(showlocals=True) - loc = {'y': 5, 'z': 7, 'x': 3, '__builtins__': {}} # __builtins__} + loc = {'y': 5, 'z': 7, 'x': 3, '@x': 2, '__builtins__': {}} reprlocals = p.repr_locals(loc) assert reprlocals.lines assert reprlocals.lines[0] == '__builtins__ = ' Repository URL: https://bitbucket.org/hpk42/py/ -- 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 Tue Jul 5 18:58:38 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Tue, 05 Jul 2011 16:58:38 -0000 Subject: [py-svn] commit/pytest: gutworth: insure moving pyc files around is atomic Message-ID: <20110705165838.6530.91485@bitbucket01.managed.contegix.com> 1 new changeset in pytest: http://bitbucket.org/hpk42/pytest/changeset/944895de1103/ changeset: 944895de1103 user: gutworth date: 2011-07-05 19:02:53 summary: insure moving pyc files around is atomic affected #: 1 file (200 bytes) --- a/_pytest/assertion/rewrite.py Tue Jul 05 18:01:31 2011 +0200 +++ b/_pytest/assertion/rewrite.py Tue Jul 05 12:02:53 2011 -0500 @@ -87,7 +87,7 @@ cache = py.path.local(fn_pypath.dirname).join("__pycache__", cache_fn) if _use_cached_pyc(fn_pypath, cache): state.trace("found cached rewritten pyc for %r" % (fn,)) - cache.copy(pyc) + _atomic_copy(cache, pyc) else: state.trace("rewriting %r" % (fn,)) _make_rewritten_pyc(state, fn_pypath, pyc) @@ -163,10 +163,16 @@ def _cache_pyc(state, pyc, cache): try: cache.dirpath().ensure(dir=True) - pyc.copy(cache) + _atomic_copy(pyc, cache) except EnvironmentError: state.trace("failed to cache %r as %r" % (pyc, cache)) +def _atomic_copy(orig, to): + """An atomic copy (at least on POSIX platforms)""" + temp = py.path.local(orig.strpath + str(os.getpid())) + orig.copy(temp) + temp.rename(to) + def rewrite_asserts(mod): """Rewrite the assert statements in mod.""" Repository URL: https://bitbucket.org/hpk42/pytest/ -- 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 Tue Jul 5 19:11:55 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Tue, 05 Jul 2011 17:11:55 -0000 Subject: [py-svn] commit/py: hpk42: up version number, add changelog entry Message-ID: <20110705171155.19843.41573@bitbucket02.managed.contegix.com> 1 new changeset in py: http://bitbucket.org/hpk42/py/changeset/790a484f09b8/ changeset: 790a484f09b8 user: hpk42 date: 2011-07-05 19:11:45 summary: up version number, add changelog entry affected #: 3 files (148 bytes) --- a/CHANGELOG Tue Jul 05 11:35:32 2011 -0500 +++ b/CHANGELOG Tue Jul 05 19:11:45 2011 +0200 @@ -1,3 +1,8 @@ +Changes between 1.4.3 and 1.4.4 +================================================== + +- a few fixes and assertion related refinements for pytest-2.1 + Changes between 1.4.2 and 1.4.3 ================================================== --- a/py/__init__.py Tue Jul 05 11:35:32 2011 -0500 +++ b/py/__init__.py Tue Jul 05 19:11:45 2011 +0200 @@ -8,7 +8,7 @@ (c) Holger Krekel and others, 2004-2010 """ -__version__ = '1.4.4.dev1' +__version__ = '1.4.4.dev2' from py import _apipkg --- a/setup.py Tue Jul 05 11:35:32 2011 -0500 +++ b/setup.py Tue Jul 05 19:11:45 2011 +0200 @@ -9,7 +9,7 @@ name='py', description='library with cross-python path, ini-parsing, io, code, log facilities', long_description = open('README.txt').read(), - version='1.4.4.dev1', + version='1.4.4.dev2', url='http://pylib.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], Repository URL: https://bitbucket.org/hpk42/py/ -- 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 Tue Jul 5 19:14:49 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Tue, 05 Jul 2011 17:14:49 -0000 Subject: [py-svn] commit/pytest: hpk42: up pytest version to 2.1.0.dev8, depend on py-1.4.4.dev2 Message-ID: <20110705171449.19843.78804@bitbucket02.managed.contegix.com> 1 new changeset in pytest: http://bitbucket.org/hpk42/pytest/changeset/903749b08439/ changeset: 903749b08439 user: hpk42 date: 2011-07-05 19:14:38 summary: up pytest version to 2.1.0.dev8, depend on py-1.4.4.dev2 affected #: 2 files (6 bytes) --- a/_pytest/__init__.py Tue Jul 05 12:02:53 2011 -0500 +++ b/_pytest/__init__.py Tue Jul 05 19:14:38 2011 +0200 @@ -1,2 +1,2 @@ # -__version__ = '2.1.0.dev7' +__version__ = '2.1.0.dev8' --- a/setup.py Tue Jul 05 12:02:53 2011 -0500 +++ b/setup.py Tue Jul 05 19:14:38 2011 +0200 @@ -22,14 +22,14 @@ name='pytest', description='py.test: simple powerful testing with Python', long_description = long_description, - version='2.1.0.dev7', + version='2.1.0.dev8', url='http://pytest.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], author='Holger Krekel, Benjamin Peterson, Ronny Pfannschmidt, Floris Bruynooghe and others', author_email='holger at merlinux.eu', entry_points= make_entry_points(), - install_requires=['py>1.4.3'], + install_requires=['py>=1.4.4.dev2'], classifiers=['Development Status :: 5 - Production/Stable', 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', Repository URL: https://bitbucket.org/hpk42/pytest/ -- 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 Tue Jul 5 21:24:09 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Tue, 05 Jul 2011 19:24:09 -0000 Subject: [py-svn] commit/pytest: hpk42: fix issue53: nose-style setup now called with the correct ordering Message-ID: <20110705192409.6528.43528@bitbucket01.managed.contegix.com> 1 new changeset in pytest: http://bitbucket.org/hpk42/pytest/changeset/afadd1bcdd7a/ changeset: afadd1bcdd7a user: hpk42 date: 2011-07-05 21:23:59 summary: fix issue53: nose-style setup now called with the correct ordering affected #: 5 files (467 bytes) --- a/CHANGELOG Tue Jul 05 19:14:38 2011 +0200 +++ b/CHANGELOG Tue Jul 05 21:23:59 2011 +0200 @@ -1,6 +1,7 @@ Changes between 2.0.3 and 2.1.0.DEV ---------------------------------------------- +- fix issue53 call nosestyle setup functions with correct ordering - fix issue58 and issue59: new assertion code fixes - merge Benjamin's assertionrewrite branch: now assertions for test modules on python 2.6 and above are done by rewriting --- a/_pytest/__init__.py Tue Jul 05 19:14:38 2011 +0200 +++ b/_pytest/__init__.py Tue Jul 05 21:23:59 2011 +0200 @@ -1,2 +1,2 @@ # -__version__ = '2.1.0.dev8' +__version__ = '2.1.0.dev9' --- a/_pytest/nose.py Tue Jul 05 19:14:38 2011 +0200 +++ b/_pytest/nose.py Tue Jul 05 21:23:59 2011 +0200 @@ -13,6 +13,7 @@ call.excinfo = call2.excinfo + at pytest.mark.trylast def pytest_runtest_setup(item): if isinstance(item, (pytest.Function)): if isinstance(item.parent, pytest.Generator): --- a/setup.py Tue Jul 05 19:14:38 2011 +0200 +++ b/setup.py Tue Jul 05 21:23:59 2011 +0200 @@ -22,7 +22,7 @@ name='pytest', description='py.test: simple powerful testing with Python', long_description = long_description, - version='2.1.0.dev8', + version='2.1.0.dev9', url='http://pytest.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], --- a/testing/test_nose.py Tue Jul 05 19:14:38 2011 +0200 +++ b/testing/test_nose.py Tue Jul 05 21:23:59 2011 +0200 @@ -255,3 +255,19 @@ result.stdout.fnmatch_lines([ "*2 passed*", ]) + +def test_nose_setup_ordering(testdir): + testdir.makepyfile(""" + def setup_module(mod): + mod.visited = True + + class TestClass: + def setup(self): + assert visited + def test_first(self): + pass + """) + result = testdir.runpytest() + result.stdout.fnmatch_lines([ + "*1 passed*", + ]) Repository URL: https://bitbucket.org/hpk42/pytest/ -- 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 Jul 6 10:19:27 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Wed, 06 Jul 2011 08:19:27 -0000 Subject: [py-svn] commit/pytest: hpk42: show release level info for pypy Message-ID: <20110706081927.6530.57205@bitbucket01.managed.contegix.com> 1 new changeset in pytest: http://bitbucket.org/hpk42/pytest/changeset/7cef8ef83b9b/ changeset: 7cef8ef83b9b user: hpk42 date: 2011-07-06 10:18:11 summary: show release level info for pypy affected #: 4 files (74 bytes) --- a/CHANGELOG Tue Jul 05 21:23:59 2011 +0200 +++ b/CHANGELOG Wed Jul 06 10:18:11 2011 +0200 @@ -13,6 +13,7 @@ - fix issue48: typo in MarkInfo repr leading to exception - fix issue49: avoid confusing error when initizaliation partially fails - fix issue44: env/username expansion for junitxml file path +- show releaselevel information for pypy Changes between 2.0.2 and 2.0.3 ---------------------------------------------- --- a/_pytest/__init__.py Tue Jul 05 21:23:59 2011 +0200 +++ b/_pytest/__init__.py Wed Jul 06 10:18:11 2011 +0200 @@ -1,2 +1,2 @@ # -__version__ = '2.1.0.dev9' +__version__ = '2.1.0.dev10' --- a/_pytest/terminal.py Tue Jul 05 21:23:59 2011 +0200 +++ b/_pytest/terminal.py Wed Jul 06 10:18:11 2011 +0200 @@ -259,7 +259,7 @@ msg = "platform %s -- Python %s" % (sys.platform, verinfo) if hasattr(sys, 'pypy_version_info'): verinfo = ".".join(map(str, sys.pypy_version_info[:3])) - msg += "[pypy-%s]" % verinfo + msg += "[pypy-%s-%s]" % (verinfo, sys.pypy_version_info[3]) msg += " -- pytest-%s" % (py.test.__version__) if self.verbosity > 0 or self.config.option.debug or \ getattr(self.config.option, 'pastebin', None): --- a/setup.py Tue Jul 05 21:23:59 2011 +0200 +++ b/setup.py Wed Jul 06 10:18:11 2011 +0200 @@ -22,7 +22,7 @@ name='pytest', description='py.test: simple powerful testing with Python', long_description = long_description, - version='2.1.0.dev9', + version='2.1.0.dev10', url='http://pytest.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], Repository URL: https://bitbucket.org/hpk42/pytest/ -- 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 Jul 6 20:22:56 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Wed, 06 Jul 2011 18:22:56 -0000 Subject: [py-svn] commit/pytest: hpk42: rearrange and streamline documentation navigation to better work Message-ID: <20110706182256.6530.28224@bitbucket01.managed.contegix.com> 1 new changeset in pytest: http://bitbucket.org/hpk42/pytest/changeset/5f32895f8c3c/ changeset: 5f32895f8c3c user: hpk42 date: 2011-07-06 20:21:59 summary: rearrange and streamline documentation navigation to better work with readthedocs and also with PDF generation. affected #: 12 files (3.3 KB) --- a/CHANGELOG Wed Jul 06 10:18:11 2011 +0200 +++ b/CHANGELOG Wed Jul 06 20:21:59 2011 +0200 @@ -13,7 +13,8 @@ - fix issue48: typo in MarkInfo repr leading to exception - fix issue49: avoid confusing error when initizaliation partially fails - fix issue44: env/username expansion for junitxml file path -- show releaselevel information for pypy +- show releaselevel information in test runs for pypy +- reworked doc pages for better navigation and PDF generation Changes between 2.0.2 and 2.0.3 ---------------------------------------------- --- a/doc/_static/sphinxdoc.css Wed Jul 06 10:18:11 2011 +0200 +++ b/doc/_static/sphinxdoc.css Wed Jul 06 20:21:59 2011 +0200 @@ -48,7 +48,7 @@ } div.related { - font-size: 1em; + font-size: 0.8em; } div.related ul { --- a/doc/_templates/indexsidebar.html Wed Jul 06 10:18:11 2011 +0200 +++ b/doc/_templates/indexsidebar.html Wed Jul 06 20:21:59 2011 +0200 @@ -10,7 +10,7 @@

{{ release }} release [Changelog]

-pytest on PyPI +pytest/PyPI

easy_install pytest
pip install pytest
--- a/doc/_templates/layout.html Wed Jul 06 10:18:11 2011 +0200 +++ b/doc/_templates/layout.html Wed Jul 06 20:21:59 2011 +0200 @@ -1,29 +1,5 @@ {% extends "!layout.html" %} -{% block relbar1 %} -{% endblock %} -{% block relbar2 %} -{% endblock %} - -{% block rootrellink %} -{% endblock %} -{% block sidebarrel %} -{% endblock %} - -{% block header %} -
-

pytest: rapid no-boilerplate testing with Python

-
- home |  - all docs |  - install |  - examples |  - customize |  - issues|  - contact  -
-
-{% endblock %} {% block footer %} {{ super() }} --- a/doc/conf.py Wed Jul 06 10:18:11 2011 +0200 +++ b/doc/conf.py Wed Jul 06 20:21:59 2011 +0200 @@ -107,10 +107,10 @@ # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". -#html_title = None +html_title = None # A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None +html_short_title = "pytest-%s" % release # The name of an image file (relative to this directory) to place at the top # of the sidebar. @@ -136,7 +136,7 @@ # Custom sidebar templates, maps document names to template names. #html_sidebars = {} -html_sidebars = {'index': 'indexsidebar.html'} +#html_sidebars = {'*': 'sidebar.html'} # Additional templates that should be rendered to pages, maps page names to # template names. @@ -145,16 +145,16 @@ # If false, no module index is generated. -#html_domain_indices = True +html_domain_indices = True # If false, no index is generated. -#html_use_index = True +html_use_index = False # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True +html_show_sourcelink = False # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True --- a/doc/contents.txt Wed Jul 06 10:18:11 2011 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,33 +0,0 @@ - -.. _toc: - -Table of Contents -======================== - -.. note:: - version 2.0 introduces :ref:`pytest as the main Python import name ` - -.. toctree:: - :maxdepth: 2 - - overview - apiref - plugins - example/index - talks - develop - announce/index - -.. toctree:: - :hidden: - - changelog.txt - naming20.txt - example/attic - -Indices and tables -================== - -* :ref:`genindex` -* :ref:`modindex` -* :ref:`search` --- a/doc/example/index.txt Wed Jul 06 10:18:11 2011 +0200 +++ b/doc/example/index.txt Wed Jul 06 20:21:59 2011 +0200 @@ -7,6 +7,10 @@ Here is a (growing) list of examples. :ref:`Contact ` us if you need more examples or have questions. Also take a look at the :ref:`comprehensive documentation ` which contains many example snippets as well. +.. note:: + + see :doc:`../getting-started` for basic introductionary examples + .. toctree:: :maxdepth: 2 --- a/doc/index.txt Wed Jul 06 10:18:11 2011 +0200 +++ b/doc/index.txt Wed Jul 06 20:21:59 2011 +0200 @@ -1,62 +1,24 @@ +.. _toc: -Welcome to ``py.test``! -============================================= +Table of Contents +======================== +.. toctree:: + :maxdepth: 2 -- **a mature full-featured testing tool** - - - runs on Posix/Windows, Python 2.4-3.2, PyPy and Jython - - continuously `tested on many Python interpreters `_ - - used in :ref:`many projects and organisations `, in test - suites ranging from 10 to 10s of thousands of tests - - has :ref:`comprehensive documentation ` - - comes with :ref:`tested examples ` - - supports :ref:`good integration practises ` - -- **provides no-boilerplate testing** - - - makes it :ref:`easy to get started `, - - refined :ref:`usage options ` - - :ref:`assert with the assert statement` - - helpful :ref:`traceback and failing assertion reporting ` - - allows :ref:`print debugging ` and :ref:`the - capturing of standard output during test execution ` - - supports :pep:`8` compliant coding styles in tests - -- **supports functional testing and complex test setups** - - - advanced :ref:`skip and xfail` - - generic :ref:`marking and test selection ` - - can :ref:`distribute tests to multiple CPUs ` through :ref:`xdist plugin ` - - can :ref:`continuously re-run failing tests ` - - many :ref:`builtin helpers ` - - flexible :ref:`Python test discovery` - - unique :ref:`dependency injection through funcargs ` - - :ref:`parametrized test functions ` - -- **integrates many common testing methods** - - - can integrate ``nose``, ``unittest.py`` and ``doctest.py`` style - tests, including running testcases made for Django and trial - - supports extended :ref:`xUnit style setup ` - - supports domain-specific :ref:`non-python tests` - - supports the generation of testing coverage reports - - `Javascript unit- and functional testing`_ - -- **extensive plugin and customization system** - - - all collection, reporting, running aspects are delegated to hook functions - - customizations can be per-directory, per-project or per PyPI released plugins - - it is easy to add command line options or do other kind of add-ons and customizations. - -.. _`Javascript unit- and functional testing`: http://pypi.python.org/pypi/oejskit - -.. _`easy`: http://bruynooghe.blogspot.com/2009/12/skipping-slow-test-by-default-in-pytest.html + overview + example/index + apiref + plugins + talks + develop + announce/index .. toctree:: :hidden: - contact.txt - contents.txt + changelog.txt + naming20.txt + example/attic --- a/doc/overview.txt Wed Jul 06 10:18:11 2011 +0200 +++ b/doc/overview.txt Wed Jul 06 20:21:59 2011 +0200 @@ -5,6 +5,7 @@ .. toctree:: :maxdepth: 2 + features.txt getting-started.txt usage.txt goodpractises.txt Repository URL: https://bitbucket.org/hpk42/pytest/ -- 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 Jul 6 20:26:04 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Wed, 06 Jul 2011 18:26:04 -0000 Subject: [py-svn] commit/pytest: hpk42: don't import py Message-ID: <20110706182604.6527.97634@bitbucket01.managed.contegix.com> 1 new changeset in pytest: http://bitbucket.org/hpk42/pytest/changeset/ee63ea1f137d/ changeset: ee63ea1f137d user: hpk42 date: 2011-07-06 20:25:54 summary: don't import py affected #: 1 file (86 bytes) --- a/doc/conf.py Wed Jul 06 20:21:59 2011 +0200 +++ b/doc/conf.py Wed Jul 06 20:25:54 2011 +0200 @@ -51,10 +51,9 @@ # The short X.Y version. version = '2.0' # The full version, including alpha/beta/rc tags. -import py, pytest +import pytest release = pytest.__version__ version = ".".join(release.split(".")[:2]) -#assert py.path.local().relto(py.path.local(pytest.__file__).dirpath().dirpath()) # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. Repository URL: https://bitbucket.org/hpk42/pytest/ -- 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 Jul 6 21:45:06 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Wed, 06 Jul 2011 19:45:06 -0000 Subject: [py-svn] commit/pytest: hpk42: releax py requirement to allow readthedocs installing pytest dev Message-ID: <20110706194506.19842.33995@bitbucket02.managed.contegix.com> 1 new changeset in pytest: http://bitbucket.org/hpk42/pytest/changeset/fc0864e317be/ changeset: fc0864e317be user: hpk42 date: 2011-07-06 21:44:57 summary: releax py requirement to allow readthedocs installing pytest dev affected #: 1 file (91 bytes) --- a/setup.py Wed Jul 06 20:25:54 2011 +0200 +++ b/setup.py Wed Jul 06 21:44:57 2011 +0200 @@ -29,7 +29,9 @@ author='Holger Krekel, Benjamin Peterson, Ronny Pfannschmidt, Floris Bruynooghe and others', author_email='holger at merlinux.eu', entry_points= make_entry_points(), - install_requires=['py>=1.4.4.dev2'], + # the following should be enabled for release + #install_requires=['py>=1.4.4'], + install_requires=['py>=1.4.3'], classifiers=['Development Status :: 5 - Production/Stable', 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', @@ -67,4 +69,4 @@ return {'console_scripts': l} if __name__ == '__main__': - main() \ No newline at end of file + main() Repository URL: https://bitbucket.org/hpk42/pytest/ -- 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 Jul 6 21:47:41 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Wed, 06 Jul 2011 19:47:41 -0000 Subject: [py-svn] commit/pytest: hpk42: fix a doc link Message-ID: <20110706194741.6530.95649@bitbucket01.managed.contegix.com> 1 new changeset in pytest: http://bitbucket.org/hpk42/pytest/changeset/153e2780bc97/ changeset: 153e2780bc97 user: hpk42 date: 2011-07-06 21:47:33 summary: fix a doc link affected #: 1 file (4 bytes) --- a/doc/getting-started.txt Wed Jul 06 21:44:57 2011 +0200 +++ b/doc/getting-started.txt Wed Jul 06 21:47:33 2011 +0200 @@ -66,7 +66,7 @@ report intermediate values of the assert expression freeing you from the need to learn the many names of `JUnit legacy methods`_. -.. _`the JUnit legacy methods`: http://docs.python.org/library/unittest.html#test-cases +.. _`JUnit legacy methods`: http://docs.python.org/library/unittest.html#test-cases .. _`assert statement`: http://docs.python.org/reference/simple_stmts.html#the-assert-statement Repository URL: https://bitbucket.org/hpk42/pytest/ -- 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 Jul 6 22:05:56 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Wed, 06 Jul 2011 20:05:56 -0000 Subject: [py-svn] commit/pytest: hpk42: reshuffle start page as per gutworth feedback Message-ID: <20110706200556.6529.31297@bitbucket01.managed.contegix.com> 1 new changeset in pytest: http://bitbucket.org/hpk42/pytest/changeset/7ff89f79120a/ changeset: 7ff89f79120a user: hpk42 date: 2011-07-06 22:05:48 summary: reshuffle start page as per gutworth feedback affected #: 6 files (2.1 KB) --- a/doc/_templates/localtoc.html Wed Jul 06 21:47:33 2011 +0200 +++ b/doc/_templates/localtoc.html Wed Jul 06 22:05:48 2011 +0200 @@ -16,9 +16,9 @@
- index + home - features + contents
install --- a/doc/conf.py Wed Jul 06 21:47:33 2011 +0200 +++ b/doc/conf.py Wed Jul 06 22:05:48 2011 +0200 @@ -38,7 +38,7 @@ #source_encoding = 'utf-8-sig' # The master toctree document. -master_doc = 'index' +master_doc = 'contents' # General information about the project. project = u'pytest' @@ -135,7 +135,7 @@ # Custom sidebar templates, maps document names to template names. #html_sidebars = {} -#html_sidebars = {'*': 'sidebar.html'} +#html_sidebars = {'index': 'indexsidebar.html'} # Additional templates that should be rendered to pages, maps page names to # template names. --- a/doc/features.txt Wed Jul 06 21:47:33 2011 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,57 +0,0 @@ - - -Feature Overview -============================================= - - -- **a mature full-featured testing tool** - - - runs on Posix/Windows, Python 2.4-3.2, PyPy and Jython - - continuously `tested on many Python interpreters `_ - - used in :ref:`many projects and organisations `, in test - suites ranging from 10 to 10s of thousands of tests - - has :ref:`comprehensive documentation ` - - comes with :ref:`tested examples ` - - supports :ref:`good integration practises ` - -- **provides no-boilerplate testing** - - - makes it :ref:`easy to get started `, - - refined :ref:`usage options ` - - :ref:`assert with the assert statement` - - helpful :ref:`traceback and failing assertion reporting ` - - allows :ref:`print debugging ` and :ref:`the - capturing of standard output during test execution ` - - supports :pep:`8` compliant coding styles in tests - -- **supports functional testing and complex test setups** - - - advanced :ref:`skip and xfail` - - generic :ref:`marking and test selection ` - - can :ref:`distribute tests to multiple CPUs ` through :ref:`xdist plugin ` - - can :ref:`continuously re-run failing tests ` - - many :ref:`builtin helpers ` - - flexible :ref:`Python test discovery` - - unique :ref:`dependency injection through funcargs ` - - :ref:`parametrized test functions ` - -- **integrates many common testing methods** - - - can integrate ``nose``, ``unittest.py`` and ``doctest.py`` style - tests, including running testcases made for Django and trial - - supports extended :ref:`xUnit style setup ` - - supports domain-specific :ref:`non-python tests` - - supports the generation of testing coverage reports - - `Javascript unit- and functional testing`_ - -- **extensive plugin and customization system** - - - all collection, reporting, running aspects are delegated to hook functions - - customizations can be per-directory, per-project or per PyPI released plugins - - it is easy to add command line options or do other kind of add-ons and customizations. - -.. _`Javascript unit- and functional testing`: http://pypi.python.org/pypi/oejskit - -.. _`easy`: http://bruynooghe.blogspot.com/2009/12/skipping-slow-test-by-default-in-pytest.html - - --- a/doc/index.txt Wed Jul 06 21:47:33 2011 +0200 +++ b/doc/index.txt Wed Jul 06 22:05:48 2011 +0200 @@ -1,24 +1,56 @@ -.. _toc: -Table of Contents -======================== +Welcome to pytest / features +============================================= -.. toctree:: - :maxdepth: 2 +- **a mature full-featured testing tool** - overview - example/index - apiref - plugins - talks - develop - announce/index + - runs on Posix/Windows, Python 2.4-3.2, PyPy and Jython + - continuously `tested on many Python interpreters `_ + - used in :ref:`many projects and organisations `, in test + suites ranging from 10 to 10s of thousands of tests + - has :ref:`comprehensive documentation ` + - comes with :ref:`tested examples ` + - supports :ref:`good integration practises ` -.. toctree:: - :hidden: +- **provides no-boilerplate testing** - changelog.txt - naming20.txt - example/attic + - makes it :ref:`easy to get started `, + - refined :ref:`usage options ` + - :ref:`assert with the assert statement` + - helpful :ref:`traceback and failing assertion reporting ` + - allows :ref:`print debugging ` and :ref:`the + capturing of standard output during test execution ` + - supports :pep:`8` compliant coding styles in tests +- **supports functional testing and complex test setups** + + - advanced :ref:`skip and xfail` + - generic :ref:`marking and test selection ` + - can :ref:`distribute tests to multiple CPUs ` through :ref:`xdist plugin ` + - can :ref:`continuously re-run failing tests ` + - many :ref:`builtin helpers ` + - flexible :ref:`Python test discovery` + - unique :ref:`dependency injection through funcargs ` + - :ref:`parametrized test functions ` + +- **integrates many common testing methods** + + - can integrate ``nose``, ``unittest.py`` and ``doctest.py`` style + tests, including running testcases made for Django and trial + - supports extended :ref:`xUnit style setup ` + - supports domain-specific :ref:`non-python tests` + - supports the generation of testing coverage reports + - `Javascript unit- and functional testing`_ + +- **extensive plugin and customization system** + + - all collection, reporting, running aspects are delegated to hook functions + - customizations can be per-directory, per-project or per PyPI released plugins + - it is easy to add command line options or do other kind of add-ons and customizations. + +.. _`Javascript unit- and functional testing`: http://pypi.python.org/pypi/oejskit + +.. _`easy`: http://bruynooghe.blogspot.com/2009/12/skipping-slow-test-by-default-in-pytest.html + + --- a/doc/overview.txt Wed Jul 06 21:47:33 2011 +0200 +++ b/doc/overview.txt Wed Jul 06 22:05:48 2011 +0200 @@ -5,7 +5,7 @@ .. toctree:: :maxdepth: 2 - features.txt + index.txt getting-started.txt usage.txt goodpractises.txt Repository URL: https://bitbucket.org/hpk42/pytest/ -- 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 Jul 7 06:20:34 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Thu, 07 Jul 2011 04:20:34 -0000 Subject: [py-svn] commit/pytest: 2 new changesets Message-ID: <20110707042034.20562.74062@bitbucket03.managed.contegix.com> 2 new changesets in pytest: http://bitbucket.org/hpk42/pytest/changeset/606d81cbbd31/ changeset: 606d81cbbd31 user: gutworth date: 2011-07-07 06:24:04 summary: simplify rewrite-on-import Use load_module on the import hook to load the rewritten module. This allows the removal of the complicated code related to copying pyc files in and out of the cache location. It also plays more nicely with parallel py.test processes like the ones found in xdist. affected #: 3 files (1.5 KB) --- a/_pytest/assertion/__init__.py Wed Jul 06 20:25:54 2011 +0200 +++ b/_pytest/assertion/__init__.py Wed Jul 06 23:24:04 2011 -0500 @@ -28,7 +28,6 @@ def __init__(self, config, mode): self.mode = mode self.trace = config.trace.root.get("assertion") - self.pycs = [] def pytest_configure(config): mode = config.getvalue("assertmode") @@ -62,8 +61,6 @@ config._assertstate.trace("configured with mode set to %r" % (mode,)) def pytest_unconfigure(config): - if config._assertstate.mode == "rewrite": - rewrite._drain_pycs(config._assertstate) hook = config._assertstate.hook if hook is not None: sys.meta_path.remove(hook) @@ -77,8 +74,6 @@ hook.set_session(session) def pytest_sessionfinish(session): - if session.config._assertstate.mode == "rewrite": - rewrite._drain_pycs(session.config._assertstate) hook = session.config._assertstate.hook if hook is not None: hook.session = None --- a/_pytest/assertion/rewrite.py Wed Jul 06 20:25:54 2011 +0200 +++ b/_pytest/assertion/rewrite.py Wed Jul 06 23:24:04 2011 -0500 @@ -8,6 +8,7 @@ import os import struct import sys +import types import py from _pytest.assertion import util @@ -22,14 +23,11 @@ del ver class AssertionRewritingHook(object): - """Import hook which rewrites asserts. - - Note this hook doesn't load modules itself. It uses find_module to write a - fake pyc, so the normal import system will find it. - """ + """Import hook which rewrites asserts.""" def __init__(self): self.session = None + self.modules = {} def set_session(self, session): self.fnpats = session.config.getini("python_files") @@ -77,37 +75,53 @@ return None finally: self.session = sess - # This looks like a test file, so rewrite it. This is the most magical - # part of the process: load the source, rewrite the asserts, and write a - # fake pyc, so that it'll be loaded when the module is imported. This is - # complicated by the fact we cache rewritten pycs. - pyc = _compute_pyc_location(fn_pypath) - state.pycs.append(pyc) - cache_fn = fn_pypath.basename[:-3] + "." + PYTEST_TAG + ".pyc" - cache = py.path.local(fn_pypath.dirname).join("__pycache__", cache_fn) - if _use_cached_pyc(fn_pypath, cache): + # The requested module looks like a test file, so rewrite it. This is + # the most magical part of the process: load the source, rewrite the + # asserts, and load the rewritten source. We also cache the rewritten + # module code in a special pyc. We must be aware of the possibility of + # concurrent py.test processes rewriting and loading pycs. To avoid + # tricky race conditions, we maintain the following invariant: The + # cached pyc is always a complete, valid pyc. Operations on it must be + # atomic. POSIX's atomic rename comes in handy. + cache_dir = os.path.join(fn_pypath.dirname, "__pycache__") + py.path.local(cache_dir).ensure(dir=True) + cache_name = fn_pypath.basename[:-3] + "." + PYTEST_TAG + ".pyc" + pyc = os.path.join(cache_dir, cache_name) + co = _read_pyc(fn_pypath, pyc) + if co is None: + state.trace("rewriting %r" % (fn,)) + co = _make_rewritten_pyc(state, fn_pypath, pyc) + if co is None: + # Probably a SyntaxError in the module. + return None + else: state.trace("found cached rewritten pyc for %r" % (fn,)) - _atomic_copy(cache, pyc) - else: - state.trace("rewriting %r" % (fn,)) - _make_rewritten_pyc(state, fn_pypath, pyc) - # Try cache it in the __pycache__ directory. - _cache_pyc(state, pyc, cache) - return None + self.modules[name] = co, pyc + return self -def _drain_pycs(state): - for pyc in state.pycs: + def load_module(self, name): + co, pyc = self.modules.pop(name) + # I wish I could just call imp.load_compiled here, but __file__ has to + # be set properly. In Python 3.2+, this all would be handled correctly + # by load_compiled. + mod = sys.modules[name] = imp.new_module(name) try: - pyc.remove() - except py.error.ENOENT: - state.trace("couldn't find pyc: %r" % (pyc,)) - else: - state.trace("removed pyc: %r" % (pyc,)) - del state.pycs[:] + mod.__file__ = co.co_filename + # Normally, this attribute is 3.2+. + mod.__cached__ = pyc + exec co in mod.__dict__ + except: + del sys.modules[name] + raise + return sys.modules[name] def _write_pyc(co, source_path, pyc): + # Technically, we don't have to have the same pyc format as (C)Python, since + # these "pycs" should never be seen by builtin import. However, there's + # little reason deviate, and I hope sometime to be able to use + # imp.load_compiled to load them. (See the comment in load_module above.) mtime = int(source_path.mtime()) - fp = pyc.open("wb") + fp = open(pyc, "wb") try: fp.write(imp.get_magic()) fp.write(struct.pack("
- index + home - features + contents
install --- a/doc/conf.py Wed Jul 06 23:24:04 2011 -0500 +++ b/doc/conf.py Wed Jul 06 23:24:54 2011 -0500 @@ -38,7 +38,7 @@ #source_encoding = 'utf-8-sig' # The master toctree document. -master_doc = 'index' +master_doc = 'contents' # General information about the project. project = u'pytest' @@ -135,7 +135,7 @@ # Custom sidebar templates, maps document names to template names. #html_sidebars = {} -#html_sidebars = {'*': 'sidebar.html'} +#html_sidebars = {'index': 'indexsidebar.html'} # Additional templates that should be rendered to pages, maps page names to # template names. --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/doc/contents.txt Wed Jul 06 23:24:54 2011 -0500 @@ -0,0 +1,24 @@ + +.. _toc: + +Table of Contents +======================== + +.. toctree:: + :maxdepth: 2 + + overview + example/index + apiref + plugins + talks + develop + announce/index + +.. toctree:: + :hidden: + + changelog.txt + naming20.txt + example/attic + --- a/doc/features.txt Wed Jul 06 23:24:04 2011 -0500 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,57 +0,0 @@ - - -Feature Overview -============================================= - - -- **a mature full-featured testing tool** - - - runs on Posix/Windows, Python 2.4-3.2, PyPy and Jython - - continuously `tested on many Python interpreters `_ - - used in :ref:`many projects and organisations `, in test - suites ranging from 10 to 10s of thousands of tests - - has :ref:`comprehensive documentation ` - - comes with :ref:`tested examples ` - - supports :ref:`good integration practises ` - -- **provides no-boilerplate testing** - - - makes it :ref:`easy to get started `, - - refined :ref:`usage options ` - - :ref:`assert with the assert statement` - - helpful :ref:`traceback and failing assertion reporting ` - - allows :ref:`print debugging ` and :ref:`the - capturing of standard output during test execution ` - - supports :pep:`8` compliant coding styles in tests - -- **supports functional testing and complex test setups** - - - advanced :ref:`skip and xfail` - - generic :ref:`marking and test selection ` - - can :ref:`distribute tests to multiple CPUs ` through :ref:`xdist plugin ` - - can :ref:`continuously re-run failing tests ` - - many :ref:`builtin helpers ` - - flexible :ref:`Python test discovery` - - unique :ref:`dependency injection through funcargs ` - - :ref:`parametrized test functions ` - -- **integrates many common testing methods** - - - can integrate ``nose``, ``unittest.py`` and ``doctest.py`` style - tests, including running testcases made for Django and trial - - supports extended :ref:`xUnit style setup ` - - supports domain-specific :ref:`non-python tests` - - supports the generation of testing coverage reports - - `Javascript unit- and functional testing`_ - -- **extensive plugin and customization system** - - - all collection, reporting, running aspects are delegated to hook functions - - customizations can be per-directory, per-project or per PyPI released plugins - - it is easy to add command line options or do other kind of add-ons and customizations. - -.. _`Javascript unit- and functional testing`: http://pypi.python.org/pypi/oejskit - -.. _`easy`: http://bruynooghe.blogspot.com/2009/12/skipping-slow-test-by-default-in-pytest.html - - --- a/doc/getting-started.txt Wed Jul 06 23:24:04 2011 -0500 +++ b/doc/getting-started.txt Wed Jul 06 23:24:54 2011 -0500 @@ -66,7 +66,7 @@ report intermediate values of the assert expression freeing you from the need to learn the many names of `JUnit legacy methods`_. -.. _`the JUnit legacy methods`: http://docs.python.org/library/unittest.html#test-cases +.. _`JUnit legacy methods`: http://docs.python.org/library/unittest.html#test-cases .. _`assert statement`: http://docs.python.org/reference/simple_stmts.html#the-assert-statement --- a/doc/index.txt Wed Jul 06 23:24:04 2011 -0500 +++ b/doc/index.txt Wed Jul 06 23:24:54 2011 -0500 @@ -1,24 +1,56 @@ -.. _toc: -Table of Contents -======================== +Welcome to pytest / features +============================================= -.. toctree:: - :maxdepth: 2 +- **a mature full-featured testing tool** - overview - example/index - apiref - plugins - talks - develop - announce/index + - runs on Posix/Windows, Python 2.4-3.2, PyPy and Jython + - continuously `tested on many Python interpreters `_ + - used in :ref:`many projects and organisations `, in test + suites ranging from 10 to 10s of thousands of tests + - has :ref:`comprehensive documentation ` + - comes with :ref:`tested examples ` + - supports :ref:`good integration practises ` -.. toctree:: - :hidden: +- **provides no-boilerplate testing** - changelog.txt - naming20.txt - example/attic + - makes it :ref:`easy to get started `, + - refined :ref:`usage options ` + - :ref:`assert with the assert statement` + - helpful :ref:`traceback and failing assertion reporting ` + - allows :ref:`print debugging ` and :ref:`the + capturing of standard output during test execution ` + - supports :pep:`8` compliant coding styles in tests +- **supports functional testing and complex test setups** + + - advanced :ref:`skip and xfail` + - generic :ref:`marking and test selection ` + - can :ref:`distribute tests to multiple CPUs ` through :ref:`xdist plugin ` + - can :ref:`continuously re-run failing tests ` + - many :ref:`builtin helpers ` + - flexible :ref:`Python test discovery` + - unique :ref:`dependency injection through funcargs ` + - :ref:`parametrized test functions ` + +- **integrates many common testing methods** + + - can integrate ``nose``, ``unittest.py`` and ``doctest.py`` style + tests, including running testcases made for Django and trial + - supports extended :ref:`xUnit style setup ` + - supports domain-specific :ref:`non-python tests` + - supports the generation of testing coverage reports + - `Javascript unit- and functional testing`_ + +- **extensive plugin and customization system** + + - all collection, reporting, running aspects are delegated to hook functions + - customizations can be per-directory, per-project or per PyPI released plugins + - it is easy to add command line options or do other kind of add-ons and customizations. + +.. _`Javascript unit- and functional testing`: http://pypi.python.org/pypi/oejskit + +.. _`easy`: http://bruynooghe.blogspot.com/2009/12/skipping-slow-test-by-default-in-pytest.html + + --- a/doc/overview.txt Wed Jul 06 23:24:04 2011 -0500 +++ b/doc/overview.txt Wed Jul 06 23:24:54 2011 -0500 @@ -5,7 +5,7 @@ .. toctree:: :maxdepth: 2 - features.txt + index.txt getting-started.txt usage.txt goodpractises.txt --- a/setup.py Wed Jul 06 23:24:04 2011 -0500 +++ b/setup.py Wed Jul 06 23:24:54 2011 -0500 @@ -29,7 +29,9 @@ author='Holger Krekel, Benjamin Peterson, Ronny Pfannschmidt, Floris Bruynooghe and others', author_email='holger at merlinux.eu', entry_points= make_entry_points(), - install_requires=['py>=1.4.4.dev2'], + # the following should be enabled for release + #install_requires=['py>=1.4.4'], + install_requires=['py>=1.4.3'], classifiers=['Development Status :: 5 - Production/Stable', 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', @@ -67,4 +69,4 @@ return {'console_scripts': l} if __name__ == '__main__': - main() \ No newline at end of file + main() Repository URL: https://bitbucket.org/hpk42/pytest/ -- 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 Jul 7 16:39:17 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Thu, 07 Jul 2011 14:39:17 -0000 Subject: [py-svn] commit/pytest: 2 new changesets Message-ID: <20110707143917.20694.41222@bitbucket01.managed.contegix.com> 2 new changesets in pytest: http://bitbucket.org/hpk42/pytest/changeset/4314c80bc935/ changeset: 4314c80bc935 user: gutworth date: 2011-07-07 16:27:40 summary: use py.builtin.exec_ affected #: 1 file (11 bytes) --- a/_pytest/assertion/rewrite.py Wed Jul 06 23:24:54 2011 -0500 +++ b/_pytest/assertion/rewrite.py Thu Jul 07 09:27:40 2011 -0500 @@ -109,7 +109,7 @@ mod.__file__ = co.co_filename # Normally, this attribute is 3.2+. mod.__cached__ = pyc - exec co in mod.__dict__ + py.builtin.exec_(co, mod.__dict__) except: del sys.modules[name] raise http://bitbucket.org/hpk42/pytest/changeset/9bbe30498225/ changeset: 9bbe30498225 user: gutworth date: 2011-07-07 16:43:39 summary: make test name shorter, so its testdir path isn't too long on windows affected #: 1 file (9 bytes) --- a/testing/test_runner_xunit.py Thu Jul 07 09:27:40 2011 -0500 +++ b/testing/test_runner_xunit.py Thu Jul 07 09:43:39 2011 -0500 @@ -186,7 +186,7 @@ ]) assert "passed" not in result.stdout.str() -def test_setup_funcarg_setup_not_called_if_outer_scope_fails(testdir): +def test_setup_funcarg_setup_when_outer_scope_fails(testdir): p = testdir.makepyfile(""" import pytest def setup_module(mod): Repository URL: https://bitbucket.org/hpk42/pytest/ -- 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 Jul 7 18:56:10 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Thu, 07 Jul 2011 16:56:10 -0000 Subject: [py-svn] commit/py: hpk42: skip a test than can only run for pytest-2.1 Message-ID: <20110707165610.10189.72118@bitbucket02.managed.contegix.com> 1 new changeset in py: http://bitbucket.org/hpk42/py/changeset/949dd5cecef6/ changeset: 949dd5cecef6 user: hpk42 date: 2011-07-07 18:55:55 summary: skip a test than can only run for pytest-2.1 affected #: 2 files (10 bytes) --- a/testing/code/test_assertion.py Tue Jul 05 19:11:45 2011 +0200 +++ b/testing/code/test_assertion.py Thu Jul 07 18:55:55 2011 +0200 @@ -248,8 +248,8 @@ @py.test.mark.skipif("sys.version_info < (2,6)") def test_assert_customizable_reprcompare(monkeypatch): - import _pytest.assertion.util - monkeypatch.setattr(_pytest.assertion.util, '_reprcompare', lambda *args: 'hello') + util = pytest.importorskip("_pytest.assertion.util") + monkeypatch.setattr(util, '_reprcompare', lambda *args: 'hello') try: assert 3 == 4 except AssertionError: --- a/tox.ini Tue Jul 05 19:11:45 2011 +0200 +++ b/tox.ini Thu Jul 07 18:55:55 2011 +0200 @@ -1,12 +1,12 @@ [tox] envlist=py26,py27,py31,py32,py27-xdist,py25,py24 -indexserver= - default=http://pypi.testrun.org +#indexserver= +# default=http://pypi.testrun.org [testenv] changedir=testing commands=py.test --confcutdir=.. -rfsxX --junitxml={envlogdir}/junit-{envname}.xml [] -deps=pytest>=2.0.0 +deps=pytest [testenv:py27-xdist] basepython=python2.7 Repository URL: https://bitbucket.org/hpk42/py/ -- 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 Jul 7 20:17:51 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Thu, 07 Jul 2011 18:17:51 -0000 Subject: [py-svn] commit/py: hpk42: read file in text mode (should fix a test failure on python31/windows) Message-ID: <20110707181751.20696.45514@bitbucket01.managed.contegix.com> 1 new changeset in py: http://bitbucket.org/hpk42/py/changeset/a784b2eae334/ changeset: a784b2eae334 user: hpk42 date: 2011-07-07 20:17:42 summary: read file in text mode (should fix a test failure on python31/windows) affected #: 1 file (1 byte) --- a/py/_builtin.py Thu Jul 07 18:55:55 2011 +0200 +++ b/py/_builtin.py Thu Jul 07 20:17:42 2011 +0200 @@ -142,7 +142,7 @@ del back elif locs is None: locs = globs - fp = open(fn, "rb") + fp = open(fn, "r") try: source = fp.read() finally: Repository URL: https://bitbucket.org/hpk42/py/ -- 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 Jul 7 21:16:15 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Thu, 07 Jul 2011 19:16:15 -0000 Subject: [py-svn] commit/pytest: hpk42: add a note about hook partial finalization issues Message-ID: <20110707191615.20697.45671@bitbucket01.managed.contegix.com> 1 new changeset in pytest: http://bitbucket.org/hpk42/pytest/changeset/53d76a1973d0/ changeset: 53d76a1973d0 user: hpk42 date: 2011-07-07 21:15:35 summary: add a note about hook partial finalization issues affected #: 1 file (253 bytes) --- a/ISSUES.txt Thu Jul 07 09:43:39 2011 -0500 +++ b/ISSUES.txt Thu Jul 07 21:15:35 2011 +0200 @@ -7,13 +7,16 @@ the protocol now - setup/teardown is called at module level. consider making calling of setup/teardown configurable -profiling / hook call optimization -------------------------------------- -tags: enhancement 2.1 +fix start/finish partial finailization problem +--------------------------------------------------------------- +tags: bug core -bench/bench.py reveals that for very quick running -unit tests the hook architecture is a bit slow. -Profile and improve hook calls. +if a configure/runtest_setup/sessionstart/... hook invocation partially +fails the sessionfinishes is not called. Each hook implementation +should better be repsonsible for registering a cleanup/finalizer +appropriately to avoid this issue. Moreover/Alternatively, we could +record which implementations of a hook succeeded and only call their +teardown. do early-teardown of test modules ----------------------------------------- Repository URL: https://bitbucket.org/hpk42/pytest/ -- 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 Jul 7 21:24:19 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Thu, 07 Jul 2011 19:24:19 -0000 Subject: [py-svn] commit/pytest: hpk42: report keyboardintterupt even if inteerrupted during sessionstartup Message-ID: <20110707192419.20562.48945@bitbucket03.managed.contegix.com> 1 new changeset in pytest: http://bitbucket.org/hpk42/pytest/changeset/3a0ef7bc4b7f/ changeset: 3a0ef7bc4b7f user: hpk42 date: 2011-07-07 21:24:09 summary: report keyboardintterupt even if inteerrupted during sessionstartup affected #: 3 files (657 bytes) --- a/CHANGELOG Thu Jul 07 21:15:35 2011 +0200 +++ b/CHANGELOG Thu Jul 07 21:24:09 2011 +0200 @@ -15,6 +15,7 @@ - fix issue44: env/username expansion for junitxml file path - show releaselevel information in test runs for pypy - reworked doc pages for better navigation and PDF generation +- report KeyboardInterrupt even if interrupted during session startup Changes between 2.0.2 and 2.0.3 ---------------------------------------------- --- a/_pytest/terminal.py Thu Jul 07 21:15:35 2011 +0200 +++ b/_pytest/terminal.py Thu Jul 07 21:24:09 2011 +0200 @@ -318,12 +318,17 @@ self.config.hook.pytest_terminal_summary(terminalreporter=self) if exitstatus == 2: self._report_keyboardinterrupt() + del self._keyboardinterrupt_memo self.summary_deselected() self.summary_stats() def pytest_keyboard_interrupt(self, excinfo): self._keyboardinterrupt_memo = excinfo.getrepr(funcargs=True) + def pytest_unconfigure(self): + if hasattr(self, '_keyboardinterrupt_memo'): + self._report_keyboardinterrupt() + def _report_keyboardinterrupt(self): excrepr = self._keyboardinterrupt_memo msg = excrepr.reprcrash.message --- a/testing/test_terminal.py Thu Jul 07 21:15:35 2011 +0200 +++ b/testing/test_terminal.py Thu Jul 07 21:24:09 2011 +0200 @@ -167,6 +167,19 @@ ]) result.stdout.fnmatch_lines(['*KeyboardInterrupt*']) + def test_keyboard_in_sessionstart(self, testdir): + testdir.makeconftest(""" + def pytest_sessionstart(): + raise KeyboardInterrupt + """) + p = testdir.makepyfile(""" + def test_foobar(): + pass + """) + + result = testdir.runpytest() + assert result.ret == 2 + result.stdout.fnmatch_lines(['*KeyboardInterrupt*']) class TestCollectonly: Repository URL: https://bitbucket.org/hpk42/pytest/ -- 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 Jul 7 23:14:42 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Thu, 07 Jul 2011 21:14:42 -0000 Subject: [py-svn] commit/pytest-xdist: hpk42: pull pytest from testrun for py26 Message-ID: <20110707211442.10191.25802@bitbucket02.managed.contegix.com> 1 new changeset in pytest-xdist: http://bitbucket.org/hpk42/pytest-xdist/changeset/52e15109500c/ changeset: 52e15109500c user: hpk42 date: 2011-07-07 23:14:34 summary: pull pytest from testrun for py26 affected #: 1 file (174 bytes) --- a/tox.ini Sun Apr 17 23:10:37 2011 +0200 +++ b/tox.ini Thu Jul 07 23:14:34 2011 +0200 @@ -1,18 +1,22 @@ [tox] envlist=py26,py31,py27,py25,py24 indexserver= - default = http://pypi.testrun.org + testrun = http://pypi.testrun.org pypi = http://pypi.python.org/simple [testenv] +changedir=testing +deps=:testrun:pytest +commands= py.test --junitxml={envlogdir}/junit-{envname}.xml [] -changedir=testing -deps=pytest -commands= py.test --junitxml={envlogdir}/junit-{envname}.xml [] +[testenv:py31] +deps=:pypi:pytest # XXX needed because ClueRelease/pip broken +[testenv:py32] +deps=:pypi:pytest # XXX needed because ClueRelease/pip broken [testenv:py26] deps= - pytest + :testrun:pytest :pypi:pexpect [pytest] Repository URL: https://bitbucket.org/hpk42/pytest-xdist/ -- 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 Jul 7 23:21:09 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Thu, 07 Jul 2011 21:21:09 -0000 Subject: [py-svn] commit/pytest: hpk42: bump version Message-ID: <20110707212109.20694.64089@bitbucket01.managed.contegix.com> 1 new changeset in pytest: http://bitbucket.org/hpk42/pytest/changeset/53db6abe84e7/ changeset: 53db6abe84e7 user: hpk42 date: 2011-07-07 23:21:01 summary: bump version affected #: 2 files (1 byte) --- a/_pytest/__init__.py Thu Jul 07 21:24:09 2011 +0200 +++ b/_pytest/__init__.py Thu Jul 07 23:21:01 2011 +0200 @@ -1,2 +1,2 @@ # -__version__ = '2.1.0.dev10' +__version__ = '2.1.0.dev11' --- a/setup.py Thu Jul 07 21:24:09 2011 +0200 +++ b/setup.py Thu Jul 07 23:21:01 2011 +0200 @@ -22,7 +22,7 @@ name='pytest', description='py.test: simple powerful testing with Python', long_description = long_description, - version='2.1.0.dev10', + version='2.1.0.dev11', url='http://pytest.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], @@ -69,4 +69,4 @@ return {'console_scripts': l} if __name__ == '__main__': - main() + main() \ No newline at end of file Repository URL: https://bitbucket.org/hpk42/pytest/ -- 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 Fri Jul 8 12:24:30 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Fri, 08 Jul 2011 10:24:30 -0000 Subject: [py-svn] commit/py: 2 new changesets Message-ID: <20110708102430.11402.68561@bitbucket01.managed.contegix.com> 2 new changesets in py: http://bitbucket.org/hpk42/py/changeset/56eaf981e1bb/ changeset: 56eaf981e1bb user: hpk42 date: 2011-07-08 12:11:04 summary: fix a reported pytest-issue (running tests on coveragepy caused internal errors): guard py.code.Code and getfslineno against bogus input and make py.code.Code work for object instance by looking up their __call__ function. affected #: 6 files (775 bytes) --- a/CHANGELOG Thu Jul 07 20:17:42 2011 +0200 +++ b/CHANGELOG Fri Jul 08 12:11:04 2011 +0200 @@ -2,6 +2,9 @@ ================================================== - a few fixes and assertion related refinements for pytest-2.1 +- guard py.code.Code and getfslineno against bogus input + and make py.code.Code objects for object instance + by looking up their __call__ function. Changes between 1.4.2 and 1.4.3 ================================================== --- a/py/__init__.py Thu Jul 07 20:17:42 2011 +0200 +++ b/py/__init__.py Fri Jul 08 12:11:04 2011 +0200 @@ -8,7 +8,7 @@ (c) Holger Krekel and others, 2004-2010 """ -__version__ = '1.4.4.dev2' +__version__ = '1.4.4.dev3' from py import _apipkg --- a/py/_code/code.py Thu Jul 07 20:17:42 2011 +0200 +++ b/py/_code/code.py Fri Jul 08 12:11:04 2011 +0200 @@ -709,7 +709,7 @@ if compile: py.builtin.builtins.compile = oldbuiltins['compile'].pop() -def getrawcode(obj): +def getrawcode(obj, trycall=True): """ return code object for given function. """ try: return obj.__code__ @@ -718,5 +718,10 @@ obj = getattr(obj, 'func_code', obj) obj = getattr(obj, 'f_code', obj) obj = getattr(obj, '__code__', obj) + if trycall and not hasattr(obj, 'co_firstlineno'): + if hasattr(obj, '__call__'): + x = getrawcode(obj.__call__, trycall=False) + if hasattr(x, 'co_firstlineno'): + return x return obj --- a/py/_code/source.py Thu Jul 07 20:17:42 2011 +0200 +++ b/py/_code/source.py Fri Jul 08 12:11:04 2011 +0200 @@ -260,9 +260,12 @@ try: code = py.code.Code(obj) except TypeError: - # fallback to - fn = (py.std.inspect.getsourcefile(obj) or - py.std.inspect.getfile(obj)) + try: + fn = (py.std.inspect.getsourcefile(obj) or + py.std.inspect.getfile(obj)) + except TypeError: + return None, None + fspath = fn and py.path.local(fn) or None if fspath: try: --- a/setup.py Thu Jul 07 20:17:42 2011 +0200 +++ b/setup.py Fri Jul 08 12:11:04 2011 +0200 @@ -9,7 +9,7 @@ name='py', description='library with cross-python path, ini-parsing, io, code, log facilities', long_description = open('README.txt').read(), - version='1.4.4.dev2', + version='1.4.4.dev3', url='http://pylib.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], --- a/testing/code/test_source.py Thu Jul 07 20:17:42 2011 +0200 +++ b/testing/code/test_source.py Fri Jul 08 12:11:04 2011 +0200 @@ -430,3 +430,16 @@ assert fspath.basename == "test_source.py" assert lineno == A_lineno + assert getfslineno(3) == (None, None) + + +def test_code_of_object_instance_with_call(): + class A: + pass + py.test.raises(TypeError, lambda: py.code.Source(A())) + class WithCall: + def __call__(self): + pass + + code = py.code.Code(WithCall()) + assert 'pass' in str(code.source()) http://bitbucket.org/hpk42/py/changeset/a1f2c4d23419/ changeset: a1f2c4d23419 user: hpk42 date: 2011-07-08 12:11:47 summary: make formatting exceptioninfo robust against invalid CWD affected #: 5 files (658 bytes) --- a/CHANGELOG Fri Jul 08 12:11:04 2011 +0200 +++ b/CHANGELOG Fri Jul 08 12:11:47 2011 +0200 @@ -5,6 +5,7 @@ - guard py.code.Code and getfslineno against bogus input and make py.code.Code objects for object instance by looking up their __call__ function. +- make exception presentation robust against invalid current cwd Changes between 1.4.2 and 1.4.3 ================================================== --- a/py/__init__.py Fri Jul 08 12:11:04 2011 +0200 +++ b/py/__init__.py Fri Jul 08 12:11:47 2011 +0200 @@ -8,7 +8,7 @@ (c) Holger Krekel and others, 2004-2010 """ -__version__ = '1.4.4.dev3' +__version__ = '1.4.4.dev4' from py import _apipkg --- a/py/_code/code.py Fri Jul 08 12:11:04 2011 +0200 +++ b/py/_code/code.py Fri Jul 08 12:11:47 2011 +0200 @@ -516,7 +516,10 @@ def _makepath(self, path): if not self.abspath: - np = py.path.local().bestrelpath(path) + try: + np = py.path.local().bestrelpath(path) + except OSError: + return path if len(np) < len(str(path)): path = np return path --- a/setup.py Fri Jul 08 12:11:04 2011 +0200 +++ b/setup.py Fri Jul 08 12:11:47 2011 +0200 @@ -9,7 +9,7 @@ name='py', description='library with cross-python path, ini-parsing, io, code, log facilities', long_description = open('README.txt').read(), - version='1.4.4.dev3', + version='1.4.4.dev4', url='http://pylib.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], --- a/testing/code/test_excinfo.py Fri Jul 08 12:11:04 2011 +0200 +++ b/testing/code/test_excinfo.py Fri Jul 08 12:11:47 2011 +0200 @@ -549,6 +549,22 @@ assert repr.reprcrash.path.endswith("mod.py") assert repr.reprcrash.message == "ValueError: 0" + def test_repr_traceback_with_invalid_cwd(self, importasmod, monkeypatch): + mod = importasmod(""" + def f(x): + raise ValueError(x) + def entry(): + f(0) + """) + excinfo = py.test.raises(ValueError, mod.entry) + + p = FormattedExcinfo() + def raiseos(): + raise OSError(2) + monkeypatch.setattr(py.std.os, 'getcwd', raiseos) + assert p._makepath(__file__) == __file__ + reprtb = p.repr_traceback(excinfo) + def test_repr_excinfo_addouterr(self, importasmod): mod = importasmod(""" def entry(): Repository URL: https://bitbucket.org/hpk42/py/ -- 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 Fri Jul 8 12:42:48 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Fri, 08 Jul 2011 10:42:48 -0000 Subject: [py-svn] commit/pytest: hpk42: fix issue 35 - provide download link and improved PDF version Message-ID: <20110708104248.11400.51765@bitbucket01.managed.contegix.com> 1 new changeset in pytest: http://bitbucket.org/hpk42/pytest/changeset/2793fddd5531/ changeset: 2793fddd5531 user: hpk42 date: 2011-07-08 12:42:26 summary: fix issue 35 - provide download link and improved PDF version affected #: 6 files (307 bytes) --- a/CHANGELOG Thu Jul 07 23:21:01 2011 +0200 +++ b/CHANGELOG Fri Jul 08 12:42:26 2011 +0200 @@ -16,6 +16,7 @@ - show releaselevel information in test runs for pypy - reworked doc pages for better navigation and PDF generation - report KeyboardInterrupt even if interrupted during session startup +- fix issue 35 - provide PDF doc version and download link from index page Changes between 2.0.2 and 2.0.3 ---------------------------------------------- --- a/_pytest/__init__.py Thu Jul 07 23:21:01 2011 +0200 +++ b/_pytest/__init__.py Fri Jul 08 12:42:26 2011 +0200 @@ -1,2 +1,2 @@ # -__version__ = '2.1.0.dev11' +__version__ = '2.1.0.dev12' --- a/doc/conf.py Thu Jul 07 23:21:01 2011 +0200 +++ b/doc/conf.py Fri Jul 08 12:42:26 2011 +0200 @@ -48,11 +48,10 @@ # |version| and |release|, also used in various other places throughout the # built documents. # -# The short X.Y version. -version = '2.0' # The full version, including alpha/beta/rc tags. import pytest release = pytest.__version__ +# The short X.Y version. version = ".".join(release.split(".")[:2]) # The language for content autogenerated by Sphinx. Refer to documentation @@ -184,7 +183,7 @@ # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ - ('index', 'pytest.tex', u'pytest Documentation', + ('contents', 'pytest.tex', u'pytest Documentation', u'holger krekel et alii', 'manual'), ] @@ -228,7 +227,7 @@ epub_title = u'pytest' epub_author = u'holger krekel et alii' epub_publisher = u'holger krekel et alii' -epub_copyright = u'2010, holger krekel et alii' +epub_copyright = u'2011, holger krekel et alii' # The language of the text. It defaults to the language option # or en if the language is not set. --- a/doc/contents.txt Thu Jul 07 23:21:01 2011 +0200 +++ b/doc/contents.txt Fri Jul 08 12:42:26 2011 +0200 @@ -1,9 +1,13 @@ .. _toc: -Table of Contents +Full pytest documenation ======================== +`Download latest version as PDF `_ + +.. `Download latest version as EPUB `_ + .. toctree:: :maxdepth: 2 --- a/doc/index.txt Thu Jul 07 23:21:01 2011 +0200 +++ b/doc/index.txt Fri Jul 08 12:42:26 2011 +0200 @@ -6,11 +6,11 @@ - **a mature full-featured testing tool** - runs on Posix/Windows, Python 2.4-3.2, PyPy and Jython + - :ref:`comprehensive online and PDF documentation ` - continuously `tested on many Python interpreters `_ - used in :ref:`many projects and organisations `, in test suites ranging from 10 to 10s of thousands of tests - - has :ref:`comprehensive documentation ` - - comes with :ref:`tested examples ` + - comes with many :ref:`tested examples ` - supports :ref:`good integration practises ` - **provides no-boilerplate testing** --- a/setup.py Thu Jul 07 23:21:01 2011 +0200 +++ b/setup.py Fri Jul 08 12:42:26 2011 +0200 @@ -22,7 +22,7 @@ name='pytest', description='py.test: simple powerful testing with Python', long_description = long_description, - version='2.1.0.dev11', + version='2.1.0.dev12', url='http://pytest.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], Repository URL: https://bitbucket.org/hpk42/pytest/ -- 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 Fri Jul 8 13:21:10 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Fri, 08 Jul 2011 11:21:10 -0000 Subject: [py-svn] commit/pytest: hpk42: adding release announcement Message-ID: <20110708112110.9446.51172@bitbucket03.managed.contegix.com> 1 new changeset in pytest: http://bitbucket.org/hpk42/pytest/changeset/265608db666f/ changeset: 265608db666f user: hpk42 date: 2011-07-08 13:16:32 summary: adding release announcement affected #: 2 files (17 bytes) --- a/doc/announce/index.txt Fri Jul 08 12:42:26 2011 +0200 +++ b/doc/announce/index.txt Fri Jul 08 13:16:32 2011 +0200 @@ -5,6 +5,7 @@ .. toctree:: :maxdepth: 2 + release-2.1.0 release-2.0.3 release-2.0.2 release-2.0.1 Repository URL: https://bitbucket.org/hpk42/pytest/ -- 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 Fri Jul 8 13:23:33 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Fri, 08 Jul 2011 11:23:33 -0000 Subject: [py-svn] commit/pytest: hpk42: finalize 2.1.0 version numbering Message-ID: <20110708112333.9449.84531@bitbucket03.managed.contegix.com> 1 new changeset in pytest: http://bitbucket.org/hpk42/pytest/changeset/0bd2f75ba86d/ changeset: 0bd2f75ba86d user: hpk42 date: 2011-07-08 13:23:12 summary: finalize 2.1.0 version numbering affected #: 2 files (53 bytes) --- a/_pytest/__init__.py Fri Jul 08 13:16:32 2011 +0200 +++ b/_pytest/__init__.py Fri Jul 08 13:23:12 2011 +0200 @@ -1,2 +1,2 @@ # -__version__ = '2.1.0.dev12' +__version__ = '2.1.0' --- a/setup.py Fri Jul 08 13:16:32 2011 +0200 +++ b/setup.py Fri Jul 08 13:23:12 2011 +0200 @@ -22,7 +22,7 @@ name='pytest', description='py.test: simple powerful testing with Python', long_description = long_description, - version='2.1.0.dev12', + version='2.1.0', url='http://pytest.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], @@ -30,8 +30,7 @@ author_email='holger at merlinux.eu', entry_points= make_entry_points(), # the following should be enabled for release - #install_requires=['py>=1.4.4'], - install_requires=['py>=1.4.3'], + install_requires=['py>=1.4.4'], classifiers=['Development Status :: 5 - Production/Stable', 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', Repository URL: https://bitbucket.org/hpk42/pytest/ -- 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 Fri Jul 8 13:23:38 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Fri, 08 Jul 2011 11:23:38 -0000 Subject: [py-svn] commit/py: hpk42: finalize 1.4.4 Message-ID: <20110708112338.9446.80640@bitbucket03.managed.contegix.com> 1 new changeset in py: http://bitbucket.org/hpk42/py/changeset/339575e9e20f/ changeset: 339575e9e20f user: hpk42 date: 2011-07-08 13:22:14 summary: finalize 1.4.4 affected #: 2 files (10 bytes) --- a/py/__init__.py Fri Jul 08 12:11:47 2011 +0200 +++ b/py/__init__.py Fri Jul 08 13:22:14 2011 +0200 @@ -8,7 +8,7 @@ (c) Holger Krekel and others, 2004-2010 """ -__version__ = '1.4.4.dev4' +__version__ = '1.4.4' from py import _apipkg --- a/setup.py Fri Jul 08 12:11:47 2011 +0200 +++ b/setup.py Fri Jul 08 13:22:14 2011 +0200 @@ -9,7 +9,7 @@ name='py', description='library with cross-python path, ini-parsing, io, code, log facilities', long_description = open('README.txt').read(), - version='1.4.4.dev4', + version='1.4.4', url='http://pylib.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], Repository URL: https://bitbucket.org/hpk42/py/ -- 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 Fri Jul 8 15:49:32 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Fri, 08 Jul 2011 13:49:32 -0000 Subject: [py-svn] commit/py: hpk42: don't consider __call__ if asked for a class Message-ID: <20110708134932.11399.53348@bitbucket01.managed.contegix.com> 1 new changeset in py: http://bitbucket.org/hpk42/py/changeset/13912770a994/ changeset: 13912770a994 user: hpk42 date: 2011-07-08 15:30:03 summary: don't consider __call__ if asked for a class affected #: 2 files (166 bytes) --- a/py/_code/code.py Fri Jul 08 13:22:14 2011 +0200 +++ b/py/_code/code.py Fri Jul 08 15:30:03 2011 +0200 @@ -722,7 +722,7 @@ obj = getattr(obj, 'f_code', obj) obj = getattr(obj, '__code__', obj) if trycall and not hasattr(obj, 'co_firstlineno'): - if hasattr(obj, '__call__'): + if hasattr(obj, '__call__') and not py.std.inspect.isclass(obj): x = getrawcode(obj.__call__, trycall=False) if hasattr(x, 'co_firstlineno'): return x --- a/testing/code/test_source.py Fri Jul 08 13:22:14 2011 +0200 +++ b/testing/code/test_source.py Fri Jul 08 15:30:03 2011 +0200 @@ -443,3 +443,8 @@ code = py.code.Code(WithCall()) assert 'pass' in str(code.source()) + + class Hello(object): + def __call__(self): + pass + py.test.raises(TypeError, lambda: py.code.Code(Hello)) Repository URL: https://bitbucket.org/hpk42/py/ -- 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 Fri Jul 8 20:13:16 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Fri, 08 Jul 2011 18:13:16 -0000 Subject: [py-svn] commit/pytest: gutworth: on windows, rename is not atomic, so utilize exclusive access to the file Message-ID: <20110708181316.20964.75977@bitbucket03.managed.contegix.com> 1 new changeset in pytest: http://bitbucket.org/hpk42/pytest/changeset/538a933e724a/ changeset: 538a933e724a user: gutworth date: 2011-07-08 20:17:42 summary: on windows, rename is not atomic, so utilize exclusive access to the file affected #: 1 file (278 bytes) --- a/_pytest/assertion/rewrite.py Fri Jul 08 13:23:12 2011 +0200 +++ b/_pytest/assertion/rewrite.py Fri Jul 08 13:17:42 2011 -0500 @@ -153,11 +153,16 @@ # assertion rewriting, but I don't know of a fast way to tell. state.trace("failed to compile: %r" % (fn,)) return None - # Dump the code object into a file specific to this process. - proc_pyc = pyc + "." + str(os.getpid()) - _write_pyc(co, fn, proc_pyc) - # Atomically replace the pyc. - os.rename(proc_pyc, pyc) + if sys.platform.startswith("win"): + # Windows grants exclusive access to open files and doesn't have atomic + # rename, so just write into the final file. + _write_pyc(co, fn, pyc) + else: + # When not on windows, assume rename is atomic. Dump the code object + # into a file specific to this process and atomically replace it. + proc_pyc = pyc + "." + str(os.getpid()) + _write_pyc(co, fn, proc_pyc) + os.rename(proc_pyc, pyc) return co def _read_pyc(source, pyc): Repository URL: https://bitbucket.org/hpk42/pytest/ -- 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 Fri Jul 8 20:48:57 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Fri, 08 Jul 2011 18:48:57 -0000 Subject: [py-svn] commit/pytest: gutworth: customize pyc tag based on implementation Message-ID: <20110708184857.20964.96431@bitbucket03.managed.contegix.com> 1 new changeset in pytest: http://bitbucket.org/hpk42/pytest/changeset/9b1d84e207cc/ changeset: 9b1d84e207cc user: gutworth date: 2011-07-08 20:53:23 summary: customize pyc tag based on implementation affected #: 1 file (153 bytes) --- a/_pytest/assertion/rewrite.py Fri Jul 08 13:17:42 2011 -0500 +++ b/_pytest/assertion/rewrite.py Fri Jul 08 13:53:23 2011 -0500 @@ -18,9 +18,15 @@ if hasattr(imp, "get_tag"): PYTEST_TAG = imp.get_tag() + "-PYTEST" else: + if hasattr(sys, "pypy_version_info"): + impl = "pypy" + elif sys.platform == "java": + impl = "jython" + else: + impl = "cpython" ver = sys.version_info - PYTEST_TAG = "cpython-" + str(ver[0]) + str(ver[1]) + "-PYTEST" - del ver + PYTEST_TAG = "%s-%s%s-PYTEST" % (impl, ver[0], ver[1]) + del ver, impl class AssertionRewritingHook(object): """Import hook which rewrites asserts.""" Repository URL: https://bitbucket.org/hpk42/pytest/ -- 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 Fri Jul 8 22:58:34 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Fri, 08 Jul 2011 20:58:34 -0000 Subject: [py-svn] commit/pytest: hpk42: only invoke distribute's use_setuptools when there is no setuptools installed Message-ID: <20110708205834.20961.50584@bitbucket03.managed.contegix.com> 1 new changeset in pytest: http://bitbucket.org/hpk42/pytest/changeset/4736be94b3eb/ changeset: 4736be94b3eb user: hpk42 date: 2011-07-08 22:58:22 summary: only invoke distribute's use_setuptools when there is no setuptools installed affected #: 2 files (86 bytes) --- a/setup.py Fri Jul 08 13:53:23 2011 -0500 +++ b/setup.py Fri Jul 08 22:58:22 2011 +0200 @@ -1,8 +1,12 @@ import os, sys -if sys.version_info >= (3,0): +try: + from setuptools import setup +except ImportError: + if sys.version_info < (3,0): + raise from distribute_setup import use_setuptools use_setuptools() -from setuptools import setup + from setuptools import setup long_description = """ cross-project testing tool for Python. @@ -68,4 +72,4 @@ return {'console_scripts': l} if __name__ == '__main__': - main() \ No newline at end of file + main() --- a/tox.ini Fri Jul 08 13:53:23 2011 -0500 +++ b/tox.ini Fri Jul 08 22:58:22 2011 +0200 @@ -4,7 +4,7 @@ indexserver= pypi = http://pypi.python.org/simple testrun = http://pypi.testrun.org - default = http://pypi.testrun.org + default = http://pypi.python.org/simple [testenv] changedir=testing Repository URL: https://bitbucket.org/hpk42/pytest/ -- 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 Fri Jul 8 23:10:06 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Fri, 08 Jul 2011 21:10:06 -0000 Subject: [py-svn] commit/pytest: hpk42: needs pypi.testrun.org for now to pull py lib Message-ID: <20110708211006.23317.46589@bitbucket01.managed.contegix.com> 1 new changeset in pytest: http://bitbucket.org/hpk42/pytest/changeset/10aacb1e74f8/ changeset: 10aacb1e74f8 user: hpk42 date: 2011-07-08 23:09:41 summary: needs pypi.testrun.org for now to pull py lib affected #: 1 file (6 bytes) --- a/tox.ini Fri Jul 08 22:58:22 2011 +0200 +++ b/tox.ini Fri Jul 08 23:09:41 2011 +0200 @@ -4,7 +4,7 @@ indexserver= pypi = http://pypi.python.org/simple testrun = http://pypi.testrun.org - default = http://pypi.python.org/simple + default = http://pypi.testrun.org [testenv] changedir=testing Repository URL: https://bitbucket.org/hpk42/pytest/ -- 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 Sat Jul 9 10:07:58 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Sat, 09 Jul 2011 08:07:58 -0000 Subject: [py-svn] commit/py: hpk42: avoid use_setuptools if setuptools is already importable Message-ID: <20110709080758.1922.1889@bitbucket02.managed.contegix.com> 1 new changeset in py: http://bitbucket.org/hpk42/py/changeset/c1f6a1039fe5/ changeset: c1f6a1039fe5 user: hpk42 date: 2011-07-09 10:07:52 summary: avoid use_setuptools if setuptools is already importable affected #: 2 files (1 byte) --- a/distribute_setup.py Fri Jul 08 15:30:03 2011 +0200 +++ b/distribute_setup.py Sat Jul 09 10:07:52 2011 +0200 @@ -46,7 +46,7 @@ args = [quote(arg) for arg in args] return os.spawnl(os.P_WAIT, sys.executable, *args) == 0 -DEFAULT_VERSION = "0.6.13" +DEFAULT_VERSION = "0.6.19" DEFAULT_URL = "http://pypi.python.org/packages/source/d/distribute/" SETUPTOOLS_FAKED_VERSION = "0.6c11" --- a/setup.py Fri Jul 08 15:30:03 2011 +0200 +++ b/setup.py Sat Jul 09 10:07:52 2011 +0200 @@ -1,8 +1,10 @@ import os, sys -if sys.version_info >= (3,0): + +try: + from setuptools import setup +except ImportError: from distribute_setup import use_setuptools use_setuptools() -from setuptools import setup def main(): setup( @@ -37,4 +39,4 @@ ) if __name__ == '__main__': - main() \ No newline at end of file + main() Repository URL: https://bitbucket.org/hpk42/py/ -- 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 Sat Jul 9 10:08:49 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Sat, 09 Jul 2011 08:08:49 -0000 Subject: [py-svn] commit/py: hpk42: fix oversight Message-ID: <20110709080849.32383.84266@bitbucket01.managed.contegix.com> 1 new changeset in py: http://bitbucket.org/hpk42/py/changeset/d9951e3bdbc7/ changeset: d9951e3bdbc7 user: hpk42 date: 2011-07-09 10:08:27 summary: fix oversight affected #: 1 file (33 bytes) --- a/setup.py Sat Jul 09 10:07:52 2011 +0200 +++ b/setup.py Sat Jul 09 10:08:27 2011 +0200 @@ -5,6 +5,7 @@ except ImportError: from distribute_setup import use_setuptools use_setuptools() + from setuptools import setup def main(): setup( Repository URL: https://bitbucket.org/hpk42/py/ -- 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 Sat Jul 9 10:10:05 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Sat, 09 Jul 2011 08:10:05 -0000 Subject: [py-svn] commit/pytest: hpk42: try use distribute always, update version Message-ID: <20110709081005.8770.50691@bitbucket03.managed.contegix.com> 1 new changeset in pytest: http://bitbucket.org/hpk42/pytest/changeset/ff1ce9482e48/ changeset: ff1ce9482e48 user: hpk42 date: 2011-07-09 10:09:04 summary: try use distribute always, update version affected #: 2 files (47 bytes) --- a/distribute_setup.py Fri Jul 08 23:09:41 2011 +0200 +++ b/distribute_setup.py Sat Jul 09 10:09:04 2011 +0200 @@ -46,7 +46,7 @@ args = [quote(arg) for arg in args] return os.spawnl(os.P_WAIT, sys.executable, *args) == 0 -DEFAULT_VERSION = "0.6.14" +DEFAULT_VERSION = "0.6.19" DEFAULT_URL = "http://pypi.python.org/packages/source/d/distribute/" SETUPTOOLS_FAKED_VERSION = "0.6c11" --- a/setup.py Fri Jul 08 23:09:41 2011 +0200 +++ b/setup.py Sat Jul 09 10:09:04 2011 +0200 @@ -2,8 +2,6 @@ try: from setuptools import setup except ImportError: - if sys.version_info < (3,0): - raise from distribute_setup import use_setuptools use_setuptools() from setuptools import setup Repository URL: https://bitbucket.org/hpk42/pytest/ -- 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 Sat Jul 9 12:02:44 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Sat, 09 Jul 2011 10:02:44 -0000 Subject: [py-svn] commit/pytest: 2 new changesets Message-ID: <20110709100244.1923.89794@bitbucket02.managed.contegix.com> 2 new changesets in pytest: http://bitbucket.org/hpk42/pytest/changeset/e5e1746a197f/ changeset: e5e1746a197f user: hpk42 date: 2011-07-09 12:02:22 summary: mention pypi package name affected #: 2 files (88 bytes) --- a/doc/getting-started.txt Sat Jul 09 10:09:04 2011 +0200 +++ b/doc/getting-started.txt Sat Jul 09 12:02:22 2011 +0200 @@ -1,7 +1,11 @@ Installation and Getting Started =================================== -**Compatibility**: Python 2.4-3.2, Jython, PyPy on Unix/Posix and Windows +**Pythons**: Python 2.4-3.2, Jython, PyPy + +**Platforms**: Unix/Posix and Windows + +**PyPI package name**: `pytest `_ .. _`getstarted`: --- a/doc/index.txt Sat Jul 09 10:09:04 2011 +0200 +++ b/doc/index.txt Sat Jul 09 12:02:22 2011 +0200 @@ -1,6 +1,6 @@ -Welcome to pytest / features +Welcome to pytest! ============================================= - **a mature full-featured testing tool** http://bitbucket.org/hpk42/pytest/changeset/2c635e60e358/ changeset: 2c635e60e358 user: hpk42 date: 2011-07-09 12:02:27 summary: Added tag 2.1.0 for changeset e5e1746a197f affected #: 1 file (47 bytes) --- a/.hgtags Sat Jul 09 12:02:22 2011 +0200 +++ b/.hgtags Sat Jul 09 12:02:27 2011 +0200 @@ -41,3 +41,4 @@ 49f11dbff725acdcc5fe3657cbcdf9ae04e25bbc 2.0.3 49f11dbff725acdcc5fe3657cbcdf9ae04e25bbc 2.0.3 363e5a5a59c803e6bc176a6f9cc4bf1a1ca2dab0 2.0.3 +e5e1746a197f0398356a43fbe2eebac9690f795d 2.1.0 Repository URL: https://bitbucket.org/hpk42/pytest/ -- 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 Sat Jul 9 13:24:06 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Sat, 09 Jul 2011 11:24:06 -0000 Subject: [py-svn] commit/pytest: hpk42: switching back to pytest.org self-hosting for docs - there is too much issues where i needed/need to poke people :/ Message-ID: <20110709112406.1923.39021@bitbucket02.managed.contegix.com> 1 new changeset in pytest: http://bitbucket.org/hpk42/pytest/changeset/d6cab2a36306/ changeset: d6cab2a36306 user: hpk42 date: 2011-07-09 13:23:58 summary: switching back to pytest.org self-hosting for docs - there is too much issues where i needed/need to poke people :/ affected #: 4 files (259 bytes) --- a/doc/Makefile Sat Jul 09 12:02:27 2011 +0200 +++ b/doc/Makefile Sat Jul 09 13:23:58 2011 +0200 @@ -39,8 +39,14 @@ clean: -rm -rf $(BUILDDIR)/* -install: clean html - rsync -avz _build/html/ code:www-pytest/ +install: html + @rsync -avz _build/html/ pytest.org:/www/pytest.org/latest + +installpdf: latexpdf + @scp $(BUILDDIR)/latex/pytest.pdf pytest.org:/www/pytest.org/latest + +installall: clean install installpdf + @echo "done" html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html --- a/doc/_templates/localtoc.html Sat Jul 09 12:02:27 2011 +0200 +++ b/doc/_templates/localtoc.html Sat Jul 09 13:23:58 2011 +0200 @@ -18,7 +18,7 @@ home - contents + TOC contents
install --- a/doc/getting-started.txt Sat Jul 09 12:02:27 2011 +0200 +++ b/doc/getting-started.txt Sat Jul 09 13:23:58 2011 +0200 @@ -7,6 +7,8 @@ **PyPI package name**: `pytest `_ +**documentation as PDF**: `download latest `_ + .. _`getstarted`: Installation --- a/doc/index.txt Sat Jul 09 12:02:27 2011 +0200 +++ b/doc/index.txt Sat Jul 09 13:23:58 2011 +0200 @@ -6,7 +6,7 @@ - **a mature full-featured testing tool** - runs on Posix/Windows, Python 2.4-3.2, PyPy and Jython - - :ref:`comprehensive online and PDF documentation ` + - :ref:`comprehensive online ` and `PDF documentation `_ - continuously `tested on many Python interpreters `_ - used in :ref:`many projects and organisations `, in test suites ranging from 10 to 10s of thousands of tests Repository URL: https://bitbucket.org/hpk42/pytest/ -- 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 Jul 11 03:58:09 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Mon, 11 Jul 2011 01:58:09 -0000 Subject: [py-svn] commit/pytest: gutworth: update assertion option names Message-ID: <20110711015809.32380.70010@bitbucket01.managed.contegix.com> 1 new changeset in pytest: http://bitbucket.org/hpk42/pytest/changeset/93b1931b7847/ changeset: 93b1931b7847 user: gutworth date: 2011-07-11 04:02:36 summary: update assertion option names affected #: 2 files (8 bytes) --- a/_pytest/assertion/newinterpret.py Sat Jul 09 13:23:58 2011 +0200 +++ b/_pytest/assertion/newinterpret.py Sun Jul 10 21:02:36 2011 -0500 @@ -53,7 +53,7 @@ if should_fail: 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 --no-assert)") + "compute assert expression before the assert or use --assert=plain)") def run(offending_line, frame=None): if frame is None: --- a/_pytest/assertion/oldinterpret.py Sat Jul 09 13:23:58 2011 +0200 +++ b/_pytest/assertion/oldinterpret.py Sun Jul 10 21:02:36 2011 -0500 @@ -482,7 +482,7 @@ if should_fail: 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)") + "compute assert expression before the assert or use --assert=plain)") else: return None Repository URL: https://bitbucket.org/hpk42/pytest/ -- 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 Jul 11 16:18:19 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Mon, 11 Jul 2011 14:18:19 -0000 Subject: [py-svn] commit/pytest: embe: Fix a typo in assertion rewriting. Message-ID: <20110711141819.1922.83277@bitbucket02.managed.contegix.com> 1 new changeset in pytest: http://bitbucket.org/hpk42/pytest/changeset/babc46921e8e/ changeset: babc46921e8e user: embe date: 2011-07-11 11:57:47 summary: Fix a typo in assertion rewriting. affected #: 2 files (165 bytes) --- a/_pytest/assertion/rewrite.py Sun Jul 10 21:02:36 2011 -0500 +++ b/_pytest/assertion/rewrite.py Mon Jul 11 11:57:47 2011 +0200 @@ -421,7 +421,7 @@ if chain: where = [] if len(chain) > 1: - cond = ast.Boolop(ast.And(), chain) + cond = ast.BoolOp(ast.And(), list(chain)) else: cond = chain[0] self.statements.append(ast.If(cond, where, [])) --- a/testing/test_assertrewrite.py Sun Jul 10 21:02:36 2011 -0500 +++ b/testing/test_assertrewrite.py Mon Jul 11 11:57:47 2011 +0200 @@ -138,6 +138,12 @@ assert f or g getmsg(f, must_pass=True) + def test_long_chain(self): + def f(): + f = g = h = lambda: True + assert f() and g() and h() + getmsg(f, must_pass=True) + def test_short_circut_evaluation(self): def f(): assert True or explode Repository URL: https://bitbucket.org/hpk42/pytest/ -- 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 Jul 11 16:21:38 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Mon, 11 Jul 2011 14:21:38 -0000 Subject: [py-svn] commit/pytest: gutworth: roll test_long_chain in with other boolop tests Message-ID: <20110711142138.32382.36121@bitbucket01.managed.contegix.com> 1 new changeset in pytest: http://bitbucket.org/hpk42/pytest/changeset/f49bdebb6345/ changeset: f49bdebb6345 user: gutworth date: 2011-07-11 16:24:07 summary: roll test_long_chain in with other boolop tests affected #: 1 file (32 bytes) --- a/testing/test_assertrewrite.py Mon Jul 11 11:57:47 2011 +0200 +++ b/testing/test_assertrewrite.py Mon Jul 11 09:24:07 2011 -0500 @@ -137,8 +137,6 @@ g = False assert f or g getmsg(f, must_pass=True) - - def test_long_chain(self): def f(): f = g = h = lambda: True assert f() and g() and h() Repository URL: https://bitbucket.org/hpk42/pytest/ -- 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 Tue Jul 12 10:47:57 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Tue, 12 Jul 2011 08:47:57 -0000 Subject: [py-svn] commit/pytest: hpk42: add benjamin's post to docs, up version, open changelog Message-ID: <20110712084757.8772.87989@bitbucket03.managed.contegix.com> 1 new changeset in pytest: http://bitbucket.org/hpk42/pytest/changeset/c5120d04011f/ changeset: c5120d04011f user: hpk42 date: 2011-07-12 10:38:02 summary: add benjamin's post to docs, up version, open changelog affected #: 5 files (475 bytes) --- a/CHANGELOG Mon Jul 11 09:24:07 2011 -0500 +++ b/CHANGELOG Tue Jul 12 10:38:02 2011 +0200 @@ -1,3 +1,8 @@ +Changes between 2.1.0 and 2.1.1.DEV +---------------------------------------------- + +- XXX + Changes between 2.0.3 and 2.1.0.DEV ---------------------------------------------- --- a/_pytest/__init__.py Mon Jul 11 09:24:07 2011 -0500 +++ b/_pytest/__init__.py Tue Jul 12 10:38:02 2011 +0200 @@ -1,2 +1,2 @@ # -__version__ = '2.1.0' +__version__ = '2.1.1.dev1' --- a/doc/assert.txt Mon Jul 11 09:24:07 2011 -0500 +++ b/doc/assert.txt Tue Jul 12 10:38:02 2011 +0200 @@ -240,6 +240,8 @@ All assert introspection can be turned off by passing ``--assert=plain``. +For further information, Benjamin Peterson wrote up `Behind the scenes of py.test's new assertion rewriting `_. + .. versionadded:: 2.1 Add assert rewriting as an alternate introspection technique. --- a/doc/talks.txt Mon Jul 11 09:24:07 2011 -0500 +++ b/doc/talks.txt Tue Jul 12 10:38:02 2011 +0200 @@ -28,6 +28,11 @@ - `parametrizing tests, generalized`_ (blog post) - `putting test-hooks into local or global plugins`_ (blog post) +assertion introspection + +- `(07/2011) Behind the scenes of py.test's new assertion rewriting + `_ + distributed testing: - `simultaneously test your code on all platforms`_ (blog entry) --- a/setup.py Mon Jul 11 09:24:07 2011 -0500 +++ b/setup.py Tue Jul 12 10:38:02 2011 +0200 @@ -24,7 +24,7 @@ name='pytest', description='py.test: simple powerful testing with Python', long_description = long_description, - version='2.1.0', + version='2.1.1.dev1', url='http://pytest.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], @@ -70,4 +70,4 @@ return {'console_scripts': l} if __name__ == '__main__': - main() + main() \ No newline at end of file Repository URL: https://bitbucket.org/hpk42/pytest/ -- 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 Tue Jul 12 17:21:57 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Tue, 12 Jul 2011 15:21:57 -0000 Subject: [py-svn] commit/pytest: 2 new changesets Message-ID: <20110712152157.8771.21031@bitbucket03.managed.contegix.com> 2 new changesets in pytest: http://bitbucket.org/hpk42/pytest/changeset/d5f8ea5f9443/ changeset: d5f8ea5f9443 user: hpk42 date: 2011-07-09 13:48:55 summary: add experimental "+1" button affected #: 2 files (162 bytes) --- a/doc/_templates/layout.html Sat Jul 09 13:23:58 2011 +0200 +++ b/doc/_templates/layout.html Sat Jul 09 13:48:55 2011 +0200 @@ -1,5 +1,9 @@ {% extends "!layout.html" %} +{% block relbaritems %} +{{ super() }} + +{% endblock %} {% block footer %} {{ super() }} @@ -16,4 +20,5 @@ })(); + {% endblock %} --- a/doc/_templates/localtoc.html Sat Jul 09 13:23:58 2011 +0200 +++ b/doc/_templates/localtoc.html Sat Jul 09 13:48:55 2011 +0200 @@ -18,7 +18,7 @@ home - TOC contents + TOC/contents
install http://bitbucket.org/hpk42/pytest/changeset/6f3f84d5e5b7/ changeset: 6f3f84d5e5b7 user: hpk42 date: 2011-07-12 17:21:48 summary: merge heads affected #: 9 files (616 bytes) --- a/CHANGELOG Sat Jul 09 13:48:55 2011 +0200 +++ b/CHANGELOG Tue Jul 12 17:21:48 2011 +0200 @@ -1,3 +1,8 @@ +Changes between 2.1.0 and 2.1.1.DEV +---------------------------------------------- + +- XXX + Changes between 2.0.3 and 2.1.0.DEV ---------------------------------------------- --- a/_pytest/__init__.py Sat Jul 09 13:48:55 2011 +0200 +++ b/_pytest/__init__.py Tue Jul 12 17:21:48 2011 +0200 @@ -1,2 +1,2 @@ # -__version__ = '2.1.0' +__version__ = '2.1.1.dev1' --- a/_pytest/assertion/newinterpret.py Sat Jul 09 13:48:55 2011 +0200 +++ b/_pytest/assertion/newinterpret.py Tue Jul 12 17:21:48 2011 +0200 @@ -53,7 +53,7 @@ if should_fail: 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 --no-assert)") + "compute assert expression before the assert or use --assert=plain)") def run(offending_line, frame=None): if frame is None: --- a/_pytest/assertion/oldinterpret.py Sat Jul 09 13:48:55 2011 +0200 +++ b/_pytest/assertion/oldinterpret.py Tue Jul 12 17:21:48 2011 +0200 @@ -482,7 +482,7 @@ if should_fail: 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)") + "compute assert expression before the assert or use --assert=plain)") else: return None --- a/_pytest/assertion/rewrite.py Sat Jul 09 13:48:55 2011 +0200 +++ b/_pytest/assertion/rewrite.py Tue Jul 12 17:21:48 2011 +0200 @@ -421,7 +421,7 @@ if chain: where = [] if len(chain) > 1: - cond = ast.Boolop(ast.And(), chain) + cond = ast.BoolOp(ast.And(), list(chain)) else: cond = chain[0] self.statements.append(ast.If(cond, where, [])) --- a/doc/assert.txt Sat Jul 09 13:48:55 2011 +0200 +++ b/doc/assert.txt Tue Jul 12 17:21:48 2011 +0200 @@ -240,6 +240,8 @@ All assert introspection can be turned off by passing ``--assert=plain``. +For further information, Benjamin Peterson wrote up `Behind the scenes of py.test's new assertion rewriting `_. + .. versionadded:: 2.1 Add assert rewriting as an alternate introspection technique. --- a/doc/talks.txt Sat Jul 09 13:48:55 2011 +0200 +++ b/doc/talks.txt Tue Jul 12 17:21:48 2011 +0200 @@ -28,6 +28,11 @@ - `parametrizing tests, generalized`_ (blog post) - `putting test-hooks into local or global plugins`_ (blog post) +assertion introspection + +- `(07/2011) Behind the scenes of py.test's new assertion rewriting + `_ + distributed testing: - `simultaneously test your code on all platforms`_ (blog entry) --- a/setup.py Sat Jul 09 13:48:55 2011 +0200 +++ b/setup.py Tue Jul 12 17:21:48 2011 +0200 @@ -24,7 +24,7 @@ name='pytest', description='py.test: simple powerful testing with Python', long_description = long_description, - version='2.1.0', + version='2.1.1.dev1', url='http://pytest.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], @@ -70,4 +70,4 @@ return {'console_scripts': l} if __name__ == '__main__': - main() + main() \ No newline at end of file --- a/testing/test_assertrewrite.py Sat Jul 09 13:48:55 2011 +0200 +++ b/testing/test_assertrewrite.py Tue Jul 12 17:21:48 2011 +0200 @@ -137,6 +137,10 @@ g = False assert f or g getmsg(f, must_pass=True) + def f(): + f = g = h = lambda: True + assert f() and g() and h() + getmsg(f, must_pass=True) def test_short_circut_evaluation(self): def f(): Repository URL: https://bitbucket.org/hpk42/pytest/ -- 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 Tue Jul 12 18:08:59 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Tue, 12 Jul 2011 16:08:59 -0000 Subject: [py-svn] commit/pytest: gutworth: add changelog note about fixing large boolops Message-ID: <20110712160859.32380.56792@bitbucket01.managed.contegix.com> 1 new changeset in pytest: http://bitbucket.org/hpk42/pytest/changeset/795d2b09e2f1/ changeset: 795d2b09e2f1 user: gutworth date: 2011-07-12 18:13:34 summary: add changelog note about fixing large boolops affected #: 1 file (66 bytes) --- a/CHANGELOG Tue Jul 12 17:21:48 2011 +0200 +++ b/CHANGELOG Tue Jul 12 11:13:34 2011 -0500 @@ -1,7 +1,7 @@ Changes between 2.1.0 and 2.1.1.DEV ---------------------------------------------- -- XXX +- fix assertion rewriting on boolean operations with 3 or more operands Changes between 2.0.3 and 2.1.0.DEV ---------------------------------------------- Repository URL: https://bitbucket.org/hpk42/pytest/ -- 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 Tue Jul 12 19:48:39 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Tue, 12 Jul 2011 17:48:39 -0000 Subject: [py-svn] commit/py: 2 new changesets Message-ID: <20110712174839.32383.40776@bitbucket01.managed.contegix.com> 2 new changesets in py: http://bitbucket.org/hpk42/py/changeset/d4f180eb9dde/ changeset: d4f180eb9dde user: hpk42 date: 2011-07-09 12:02:53 summary: Added tag 1.4.4 for changeset d9951e3bdbc7 affected #: 1 file (47 bytes) --- a/.hgtags Sat Jul 09 10:08:27 2011 +0200 +++ b/.hgtags Sat Jul 09 12:02:53 2011 +0200 @@ -35,3 +35,4 @@ 1f3125cba7976538952be268f107c1d0c36c5ce8 1.4.1 04ab22db4ff737cf31e91d75a0f5d7077f324167 1.4.2 9950bf9d684a984d511795013421c89c5cf88bef 1.4.3 +d9951e3bdbc765e73835ae13012f6a074d13d8bf 1.4.4 http://bitbucket.org/hpk42/py/changeset/df4cc184ff65/ changeset: df4cc184ff65 user: hpk42 date: 2011-07-12 19:48:31 summary: improve unicode handling affected #: 7 files (777 bytes) --- a/CHANGELOG Sat Jul 09 12:02:53 2011 +0200 +++ b/CHANGELOG Tue Jul 12 19:48:31 2011 +0200 @@ -1,3 +1,9 @@ +Changes between 1.4.4 and 1.4.5.dev1 +================================================== + +- improve some unicode handling in terminalwriter and capturing + (used by pytest) + Changes between 1.4.3 and 1.4.4 ================================================== --- a/py/__init__.py Sat Jul 09 12:02:53 2011 +0200 +++ b/py/__init__.py Tue Jul 12 19:48:31 2011 +0200 @@ -8,7 +8,7 @@ (c) Holger Krekel and others, 2004-2010 """ -__version__ = '1.4.4' +__version__ = '1.4.5.dev1' from py import _apipkg --- a/py/_io/capture.py Sat Jul 09 12:02:53 2011 +0200 +++ b/py/_io/capture.py Tue Jul 12 19:48:31 2011 +0200 @@ -258,6 +258,9 @@ f = getattr(self, name).tmpfile f.seek(0) res = f.read() + enc = getattr(f, 'encoding', None) + if enc: + res = py.builtin._totext(res, enc) f.truncate(0) f.seek(0) l.append(res) --- a/py/_io/terminalwriter.py Sat Jul 09 12:02:53 2011 +0200 +++ b/py/_io/terminalwriter.py Tue Jul 12 19:48:31 2011 +0200 @@ -112,11 +112,9 @@ self.stringio = file = py.io.TextIO() else: file = py.std.sys.stdout - if hasattr(file, 'encoding'): - encoding = file.encoding elif hasattr(file, '__call__'): file = WriteFile(file, encoding=encoding) - self.encoding = encoding + self.encoding = encoding or getattr(file, 'encoding', "utf-8") self._file = file self.fullwidth = get_terminal_width() self.hasmarkup = should_do_markup(file) --- a/setup.py Sat Jul 09 12:02:53 2011 +0200 +++ b/setup.py Tue Jul 12 19:48:31 2011 +0200 @@ -12,7 +12,7 @@ name='py', description='library with cross-python path, ini-parsing, io, code, log facilities', long_description = open('README.txt').read(), - version='1.4.4', + version='1.4.5.dev1', url='http://pylib.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], @@ -40,4 +40,4 @@ ) if __name__ == '__main__': - main() + main() \ No newline at end of file --- a/testing/io_/test_capture.py Sat Jul 09 12:02:53 2011 +0200 +++ b/testing/io_/test_capture.py Tue Jul 12 19:48:31 2011 +0200 @@ -213,6 +213,12 @@ out, err = cap.reset() assert err == "error2" + def test_capturing_readouterr_unicode(self): + cap = self.getcapture() + print ("hx\xc4\x85\xc4\x87") + out, err = cap.readouterr() + assert out == py.builtin._totext("hx\xc4\x85\xc4\x87\n", "utf8") + def test_capturing_mixed(self): cap = self.getcapture(mixed=True) sys.stdout.write("hello ") --- a/testing/io_/test_terminalwriter.py Sat Jul 09 12:02:53 2011 +0200 +++ b/testing/io_/test_terminalwriter.py Tue Jul 12 19:48:31 2011 +0200 @@ -62,6 +62,11 @@ finally: monkeypatch.undo() +def test_terminalwriter_file_unicode(tmpdir): + f = py.std.codecs.open(str(tmpdir.join("xyz")), "wb", "utf8") + tw = py.io.TerminalWriter(file=f) + assert tw.encoding == "utf8" + def test_unicode_encoding(): msg = py.builtin._totext('b\u00f6y', 'utf8') for encoding in 'utf8', 'latin1': Repository URL: https://bitbucket.org/hpk42/py/ -- 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 Tue Jul 12 23:09:41 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Tue, 12 Jul 2011 21:09:41 -0000 Subject: [py-svn] commit/pytest: hpk42: fix #59: provide better Jenkins stdout and stderr sections Message-ID: <20110712210941.1923.36641@bitbucket02.managed.contegix.com> 1 new changeset in pytest: http://bitbucket.org/hpk42/pytest/changeset/d32c80505d56/ changeset: d32c80505d56 user: hpk42 date: 2011-07-12 23:09:03 summary: fix #59: provide better Jenkins stdout and stderr sections affected #: 9 files (1.5 KB) --- a/CHANGELOG Tue Jul 12 11:13:34 2011 -0500 +++ b/CHANGELOG Tue Jul 12 23:09:03 2011 +0200 @@ -1,6 +1,7 @@ Changes between 2.1.0 and 2.1.1.DEV ---------------------------------------------- +- fix issue59: provide system-out/err tags for junitxml output - fix assertion rewriting on boolean operations with 3 or more operands Changes between 2.0.3 and 2.1.0.DEV --- a/_pytest/__init__.py Tue Jul 12 11:13:34 2011 -0500 +++ b/_pytest/__init__.py Tue Jul 12 23:09:03 2011 +0200 @@ -1,2 +1,2 @@ # -__version__ = '2.1.1.dev1' +__version__ = '2.1.1.dev2' --- a/_pytest/capture.py Tue Jul 12 11:13:34 2011 -0500 +++ b/_pytest/capture.py Tue Jul 12 23:09:03 2011 +0200 @@ -12,12 +12,9 @@ help="shortcut for --capture=no.") def addouterr(rep, outerr): - repr = getattr(rep, 'longrepr', None) - if not hasattr(repr, 'addsection'): - return for secname, content in zip(["out", "err"], outerr): if content: - repr.addsection("Captured std%s" % secname, content.rstrip()) + rep.sections.append(("Captured std%s" % secname, content)) def pytest_unconfigure(config): # registered in config.py during early conftest.py loading --- a/_pytest/junitxml.py Tue Jul 12 11:13:34 2011 -0500 +++ b/_pytest/junitxml.py Tue Jul 12 23:09:03 2011 +0200 @@ -114,8 +114,16 @@ '') self.skipped += 1 else: - self.appendlog('%s', - report.longrepr) + sec = dict(report.sections) + fmt = '%s' + args = [report.longrepr] + for name in ('out', 'err'): + content = sec.get("Captured std%s" % name) + if content: + fmt += "%%s" % (name, name) + args.append(content) + fmt += "" + self.appendlog(fmt, *args) self.failed += 1 self._closetestcase() --- a/_pytest/runner.py Tue Jul 12 11:13:34 2011 -0500 +++ b/_pytest/runner.py Tue Jul 12 23:09:03 2011 +0200 @@ -167,7 +167,7 @@ they fail). """ def __init__(self, nodeid, location, - keywords, outcome, longrepr, when): + keywords, outcome, longrepr, when, sections=()): #: normalized collection node id self.nodeid = nodeid @@ -189,6 +189,10 @@ #: one of 'setup', 'call', 'teardown' to indicate runtest phase. self.when = when + #: list of (secname, data) extra information which needs to + #: marshallable + self.sections = list(sections) + def __repr__(self): return "" % ( self.nodeid, self.when, self.outcome) @@ -198,6 +202,7 @@ when = "teardown" def __init__(self, longrepr): self.longrepr = longrepr + self.sections = [] def pytest_make_collect_report(collector): call = CallInfo(collector._memocollect, "memocollect") @@ -219,11 +224,12 @@ getattr(call, 'result', None)) class CollectReport(BaseReport): - def __init__(self, nodeid, outcome, longrepr, result): + def __init__(self, nodeid, outcome, longrepr, result, sections=()): self.nodeid = nodeid self.outcome = outcome self.longrepr = longrepr self.result = result or [] + self.sections = list(sections) @property def location(self): --- a/_pytest/terminal.py Tue Jul 12 11:13:34 2011 -0500 +++ b/_pytest/terminal.py Tue Jul 12 23:09:03 2011 +0200 @@ -393,7 +393,7 @@ else: msg = self._getfailureheadline(rep) self.write_sep("_", msg) - rep.toterminal(self._tw) + self._outrep_summary(rep) def summary_errors(self): if self.config.option.tbstyle != "no": @@ -411,7 +411,15 @@ elif rep.when == "teardown": msg = "ERROR at teardown of " + msg self.write_sep("_", msg) - rep.toterminal(self._tw) + self._outrep_summary(rep) + + def _outrep_summary(self, rep): + rep.toterminal(self._tw) + for secname, content in rep.sections: + self._tw.sep("-", secname) + if content[-1:] == "\n": + content = content[:-1] + self._tw.line(content) def summary_stats(self): session_duration = py.std.time.time() - self._sessionstarttime --- a/setup.py Tue Jul 12 11:13:34 2011 -0500 +++ b/setup.py Tue Jul 12 23:09:03 2011 +0200 @@ -24,7 +24,7 @@ name='pytest', description='py.test: simple powerful testing with Python', long_description = long_description, - version='2.1.1.dev1', + version='2.1.1.dev2', url='http://pytest.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], @@ -32,7 +32,7 @@ author_email='holger at merlinux.eu', entry_points= make_entry_points(), # the following should be enabled for release - install_requires=['py>=1.4.4'], + install_requires=['py>=1.4.5.dev1'], classifiers=['Development Status :: 5 - Production/Stable', 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', --- a/testing/test_junitxml.py Tue Jul 12 11:13:34 2011 -0500 +++ b/testing/test_junitxml.py Tue Jul 12 23:09:03 2011 +0200 @@ -131,7 +131,14 @@ assert "Division" in fnode.toxml() def test_failure_function(self, testdir): - testdir.makepyfile("def test_fail(): raise ValueError(42)") + testdir.makepyfile(""" + import sys + def test_fail(): + print ("hello-stdout") + sys.stderr.write("hello-stderr\\n") + raise ValueError(42) + """) + result, dom = runandparse(testdir) assert result.ret node = dom.getElementsByTagName("testsuite")[0] @@ -143,6 +150,10 @@ fnode = tnode.getElementsByTagName("failure")[0] assert_attr(fnode, message="test failure") assert "ValueError" in fnode.toxml() + systemout = fnode.getElementsByTagName("system-out")[0] + assert "hello-stdout" in systemout.toxml() + systemerr = fnode.getElementsByTagName("system-err")[0] + assert "hello-stderr" in systemerr.toxml() def test_failure_escape(self, testdir): testdir.makepyfile(""" --- a/testing/test_terminal.py Tue Jul 12 11:13:34 2011 -0500 +++ b/testing/test_terminal.py Tue Jul 12 23:09:03 2011 +0200 @@ -258,10 +258,13 @@ def test_repr_python_version(monkeypatch): - monkeypatch.setattr(sys, 'version_info', (2, 5, 1, 'final', 0)) - assert repr_pythonversion() == "2.5.1-final-0" - py.std.sys.version_info = x = (2,3) - assert repr_pythonversion() == str(x) + try: + monkeypatch.setattr(sys, 'version_info', (2, 5, 1, 'final', 0)) + assert repr_pythonversion() == "2.5.1-final-0" + py.std.sys.version_info = x = (2,3) + assert repr_pythonversion() == str(x) + finally: + monkeypatch.undo() # do this early as pytest can get confused class TestFixtureReporting: def test_setup_fixture_error(self, testdir): Repository URL: https://bitbucket.org/hpk42/pytest/ -- 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 Jul 13 00:04:43 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Tue, 12 Jul 2011 22:04:43 -0000 Subject: [py-svn] commit/pytest: gutworth: fix assertion rewriting in read-only directories (refs #60) Message-ID: <20110712220443.8772.76700@bitbucket03.managed.contegix.com> 1 new changeset in pytest: http://bitbucket.org/hpk42/pytest/changeset/81082bcf2765/ changeset: 81082bcf2765 user: gutworth date: 2011-07-13 00:09:14 summary: fix assertion rewriting in read-only directories (refs #60) affected #: 3 files (688 bytes) --- a/CHANGELOG Tue Jul 12 23:09:03 2011 +0200 +++ b/CHANGELOG Tue Jul 12 17:09:14 2011 -0500 @@ -1,6 +1,7 @@ Changes between 2.1.0 and 2.1.1.DEV ---------------------------------------------- +- fix assertion rewriting in read-only directories - fix issue59: provide system-out/err tags for junitxml output - fix assertion rewriting on boolean operations with 3 or more operands --- a/_pytest/assertion/rewrite.py Tue Jul 12 23:09:03 2011 +0200 +++ b/_pytest/assertion/rewrite.py Tue Jul 12 17:09:14 2011 -0500 @@ -90,16 +90,26 @@ # cached pyc is always a complete, valid pyc. Operations on it must be # atomic. POSIX's atomic rename comes in handy. cache_dir = os.path.join(fn_pypath.dirname, "__pycache__") - py.path.local(cache_dir).ensure(dir=True) + try: + py.path.local(cache_dir).ensure(dir=True) + except py.error.EACCES: + state.trace("read only directory: %r" % (fn_pypath.dirname,)) + write = False + else: + write = True cache_name = fn_pypath.basename[:-3] + "." + PYTEST_TAG + ".pyc" pyc = os.path.join(cache_dir, cache_name) + # Notice that even if we're in a read-only directory, I'm going to check + # for a cached pyc. This may not be optimal... co = _read_pyc(fn_pypath, pyc) if co is None: state.trace("rewriting %r" % (fn,)) - co = _make_rewritten_pyc(state, fn_pypath, pyc) + co = _rewrite_test(state, fn_pypath) if co is None: - # Probably a SyntaxError in the module. + # Probably a SyntaxError in the test. return None + if write: + _make_rewritten_pyc(state, fn_pypath, pyc, co) else: state.trace("found cached rewritten pyc for %r" % (fn,)) self.modules[name] = co, pyc @@ -135,12 +145,8 @@ finally: fp.close() -def _make_rewritten_pyc(state, fn, pyc): - """Try to rewrite *fn* and dump the rewritten code to *pyc*. - - Return the code object of the rewritten module on success. Return None if - there are problems parsing or compiling the module. - """ +def _rewrite_test(state, fn): + """Try to read and rewrite *fn* and return the code object.""" try: source = fn.read("rb") except EnvironmentError: @@ -159,6 +165,10 @@ # assertion rewriting, but I don't know of a fast way to tell. state.trace("failed to compile: %r" % (fn,)) return None + return co + +def _make_rewritten_pyc(state, fn, pyc, co): + """Try to dump rewritten code to *pyc*.""" if sys.platform.startswith("win"): # Windows grants exclusive access to open files and doesn't have atomic # rename, so just write into the final file. --- a/testing/test_assertrewrite.py Tue Jul 12 23:09:03 2011 +0200 +++ b/testing/test_assertrewrite.py Tue Jul 12 17:09:14 2011 -0500 @@ -266,3 +266,14 @@ return False assert myany(A() < 0) assert "< 0" in getmsg(f) + + +class TestRewriteOnImport: + + def test_readonly(self, testdir): + sub = testdir.mkdir("testing") + sub.join("test_readonly.py").write(""" +def test_rewritten(): + assert "@py_builtins" in globals()""") + sub.chmod(320) + assert testdir.runpytest().ret == 0 Repository URL: https://bitbucket.org/hpk42/pytest/ -- 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 Jul 13 18:48:34 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Wed, 13 Jul 2011 16:48:34 -0000 Subject: [py-svn] commit/pytest: hpk42: put systemout/systemerr to correct xml location Message-ID: <20110713164834.14068.88634@bitbucket03.managed.contegix.com> 1 new changeset in pytest: http://bitbucket.org/hpk42/pytest/changeset/dc450bd494a6/ changeset: dc450bd494a6 user: hpk42 date: 2011-07-13 18:47:27 summary: put systemout/systemerr to correct xml location affected #: 4 files (103 bytes) --- a/_pytest/__init__.py Tue Jul 12 17:09:14 2011 -0500 +++ b/_pytest/__init__.py Wed Jul 13 18:47:27 2011 +0200 @@ -1,2 +1,2 @@ # -__version__ = '2.1.1.dev2' +__version__ = '2.1.1.dev3' --- a/_pytest/junitxml.py Tue Jul 12 17:09:14 2011 -0500 +++ b/_pytest/junitxml.py Wed Jul 13 18:47:27 2011 +0200 @@ -115,15 +115,13 @@ self.skipped += 1 else: sec = dict(report.sections) - fmt = '%s' - args = [report.longrepr] + self.appendlog('%s', + report.longrepr) for name in ('out', 'err'): content = sec.get("Captured std%s" % name) if content: - fmt += "%%s" % (name, name) - args.append(content) - fmt += "" - self.appendlog(fmt, *args) + self.appendlog( + "%%s" % (name, name), content) self.failed += 1 self._closetestcase() --- a/setup.py Tue Jul 12 17:09:14 2011 -0500 +++ b/setup.py Wed Jul 13 18:47:27 2011 +0200 @@ -24,7 +24,7 @@ name='pytest', description='py.test: simple powerful testing with Python', long_description = long_description, - version='2.1.1.dev2', + version='2.1.1.dev3', url='http://pytest.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], --- a/testing/test_junitxml.py Tue Jul 12 17:09:14 2011 -0500 +++ b/testing/test_junitxml.py Wed Jul 13 18:47:27 2011 +0200 @@ -150,9 +150,11 @@ fnode = tnode.getElementsByTagName("failure")[0] assert_attr(fnode, message="test failure") assert "ValueError" in fnode.toxml() - systemout = fnode.getElementsByTagName("system-out")[0] + systemout = fnode.nextSibling + assert systemout.tagName == "system-out" assert "hello-stdout" in systemout.toxml() - systemerr = fnode.getElementsByTagName("system-err")[0] + systemerr = systemout.nextSibling + assert systemerr.tagName == "system-err" assert "hello-stderr" in systemerr.toxml() def test_failure_escape(self, testdir): Repository URL: https://bitbucket.org/hpk42/pytest/ -- 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 Jul 13 20:29:44 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Wed, 13 Jul 2011 18:29:44 -0000 Subject: [py-svn] commit/pytest: 2 new changesets Message-ID: <20110713182944.14070.55958@bitbucket03.managed.contegix.com> 2 new changesets in pytest: http://bitbucket.org/hpk42/pytest/changeset/873a5c8e75a1/ changeset: 873a5c8e75a1 user: gutworth date: 2011-07-13 20:33:54 summary: respect sys.dont_write_bytecode and PYTHONDONTWRITEBYTECODE affected #: 3 files (485 bytes) --- a/CHANGELOG Tue Jul 12 17:09:14 2011 -0500 +++ b/CHANGELOG Wed Jul 13 13:33:54 2011 -0500 @@ -1,6 +1,7 @@ Changes between 2.1.0 and 2.1.1.DEV ---------------------------------------------- +- don't cache rewritten modules if bytecode generation is disabled - fix assertion rewriting in read-only directories - fix issue59: provide system-out/err tags for junitxml output - fix assertion rewriting on boolean operations with 3 or more operands --- a/_pytest/assertion/rewrite.py Tue Jul 12 17:09:14 2011 -0500 +++ b/_pytest/assertion/rewrite.py Wed Jul 13 13:33:54 2011 -0500 @@ -89,14 +89,14 @@ # tricky race conditions, we maintain the following invariant: The # cached pyc is always a complete, valid pyc. Operations on it must be # atomic. POSIX's atomic rename comes in handy. + write = not sys.dont_write_bytecode cache_dir = os.path.join(fn_pypath.dirname, "__pycache__") - try: - py.path.local(cache_dir).ensure(dir=True) - except py.error.EACCES: - state.trace("read only directory: %r" % (fn_pypath.dirname,)) - write = False - else: - write = True + if write: + try: + py.path.local(cache_dir).ensure(dir=True) + except py.error.EACCES: + state.trace("read only directory: %r" % (fn_pypath.dirname,)) + write = False cache_name = fn_pypath.basename[:-3] + "." + PYTEST_TAG + ".pyc" pyc = os.path.join(cache_dir, cache_name) # Notice that even if we're in a read-only directory, I'm going to check --- a/testing/test_assertrewrite.py Tue Jul 12 17:09:14 2011 -0500 +++ b/testing/test_assertrewrite.py Wed Jul 13 13:33:54 2011 -0500 @@ -277,3 +277,13 @@ assert "@py_builtins" in globals()""") sub.chmod(320) assert testdir.runpytest().ret == 0 + + def test_dont_write_bytecode(self, testdir, monkeypatch): + testdir.makepyfile(""" +import os +def test_no_bytecode(): + assert "__pycache__" in __cached__ + assert not os.path.exists(__cached__) + assert not os.path.exists(os.path.dirname(__cached__))""") + monkeypatch.setenv("PYTHONDONTWRITEBYTECODE", "1") + assert testdir.runpytest().ret == 0 http://bitbucket.org/hpk42/pytest/changeset/88a3b8eab308/ changeset: 88a3b8eab308 user: gutworth date: 2011-07-13 20:34:24 summary: merge heads affected #: 4 files (103 bytes) --- a/_pytest/__init__.py Wed Jul 13 13:33:54 2011 -0500 +++ b/_pytest/__init__.py Wed Jul 13 13:34:24 2011 -0500 @@ -1,2 +1,2 @@ # -__version__ = '2.1.1.dev2' +__version__ = '2.1.1.dev3' --- a/_pytest/junitxml.py Wed Jul 13 13:33:54 2011 -0500 +++ b/_pytest/junitxml.py Wed Jul 13 13:34:24 2011 -0500 @@ -115,15 +115,13 @@ self.skipped += 1 else: sec = dict(report.sections) - fmt = '%s' - args = [report.longrepr] + self.appendlog('%s', + report.longrepr) for name in ('out', 'err'): content = sec.get("Captured std%s" % name) if content: - fmt += "%%s" % (name, name) - args.append(content) - fmt += "" - self.appendlog(fmt, *args) + self.appendlog( + "%%s" % (name, name), content) self.failed += 1 self._closetestcase() --- a/setup.py Wed Jul 13 13:33:54 2011 -0500 +++ b/setup.py Wed Jul 13 13:34:24 2011 -0500 @@ -24,7 +24,7 @@ name='pytest', description='py.test: simple powerful testing with Python', long_description = long_description, - version='2.1.1.dev2', + version='2.1.1.dev3', url='http://pytest.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], --- a/testing/test_junitxml.py Wed Jul 13 13:33:54 2011 -0500 +++ b/testing/test_junitxml.py Wed Jul 13 13:34:24 2011 -0500 @@ -150,9 +150,11 @@ fnode = tnode.getElementsByTagName("failure")[0] assert_attr(fnode, message="test failure") assert "ValueError" in fnode.toxml() - systemout = fnode.getElementsByTagName("system-out")[0] + systemout = fnode.nextSibling + assert systemout.tagName == "system-out" assert "hello-stdout" in systemout.toxml() - systemerr = fnode.getElementsByTagName("system-err")[0] + systemerr = systemout.nextSibling + assert systemerr.tagName == "system-err" assert "hello-stderr" in systemerr.toxml() def test_failure_escape(self, testdir): Repository URL: https://bitbucket.org/hpk42/pytest/ -- 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 Jul 14 18:41:45 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Thu, 14 Jul 2011 16:41:45 -0000 Subject: [py-svn] commit/pytest: 2 new changesets Message-ID: <20110714164145.14317.74908@bitbucket01.managed.contegix.com> 2 new changesets in pytest: http://bitbucket.org/hpk42/pytest/changeset/4b05174b6773/ changeset: 4b05174b6773 user: gutworth date: 2011-07-14 18:45:42 summary: fix assertion rewriting on calls with a double-star arg affected #: 3 files (178 bytes) --- a/CHANGELOG Wed Jul 13 13:34:24 2011 -0500 +++ b/CHANGELOG Thu Jul 14 11:45:42 2011 -0500 @@ -1,6 +1,7 @@ Changes between 2.1.0 and 2.1.1.DEV ---------------------------------------------- +- fix assertion rewriting on calls with a ** arg - don't cache rewritten modules if bytecode generation is disabled - fix assertion rewriting in read-only directories - fix issue59: provide system-out/err tags for junitxml output --- a/_pytest/assertion/rewrite.py Wed Jul 13 13:34:24 2011 -0500 +++ b/_pytest/assertion/rewrite.py Thu Jul 14 11:45:42 2011 -0500 @@ -514,7 +514,7 @@ new_star, expl = self.visit(call.starargs) arg_expls.append("*" + expl) if call.kwargs: - new_kwarg, expl = self.visit(call.kwarg) + new_kwarg, expl = self.visit(call.kwargs) arg_expls.append("**" + expl) expl = "%s(%s)" % (func_expl, ', '.join(arg_expls)) new_call = ast.Call(new_func, new_args, new_kwargs, new_star, new_kwarg) --- a/testing/test_assertrewrite.py Wed Jul 13 13:34:24 2011 -0500 +++ b/testing/test_assertrewrite.py Thu Jul 14 11:45:42 2011 -0500 @@ -195,6 +195,10 @@ def f(): assert g(1, 3, g=23) assert getmsg(f, ns) == """assert g(1, 3, g=23)""" + def f(): + x = "a" + assert g(**{x : 2}) + assert getmsg(f, ns) == """assert g(**{'a': 2})""" def test_attribute(self): class X(object): http://bitbucket.org/hpk42/pytest/changeset/a7db87f7a262/ changeset: a7db87f7a262 user: gutworth date: 2011-07-14 18:46:32 summary: add a test for vararg call affected #: 1 file (131 bytes) --- a/testing/test_assertrewrite.py Thu Jul 14 11:45:42 2011 -0500 +++ b/testing/test_assertrewrite.py Thu Jul 14 11:46:32 2011 -0500 @@ -196,6 +196,10 @@ assert g(1, 3, g=23) assert getmsg(f, ns) == """assert g(1, 3, g=23)""" def f(): + seq = [1, 2, 3] + assert g(*seq) + assert getmsg(f, ns) == """assert g(*[1, 2, 3])""" + def f(): x = "a" assert g(**{x : 2}) assert getmsg(f, ns) == """assert g(**{'a': 2})""" Repository URL: https://bitbucket.org/hpk42/pytest/ -- 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 Jul 14 19:12:13 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Thu, 14 Jul 2011 17:12:13 -0000 Subject: [py-svn] commit/pytest: hpk42: enhance debug tracing: print trace tags at the end of message and forget about "prefix". Message-ID: <20110714171213.25348.9523@bitbucket02.managed.contegix.com> 1 new changeset in pytest: http://bitbucket.org/hpk42/pytest/changeset/6247ce817d98/ changeset: 6247ce817d98 user: hpk42 date: 2011-07-14 19:11:50 summary: enhance debug tracing: print trace tags at the end of message and forget about "prefix". Always log to "pytestdebug.log" if "--debug" option is given. also move related code to pytest_helpconfig plugin. affected #: 7 files (1.8 KB) --- a/_pytest/config.py Thu Jul 14 11:46:32 2011 -0500 +++ b/_pytest/config.py Thu Jul 14 19:11:50 2011 +0200 @@ -8,8 +8,6 @@ def pytest_cmdline_parse(pluginmanager, args): config = Config(pluginmanager) config.parse(args) - if config.option.debug: - config.trace.root.setwriter(sys.stderr.write) return config def pytest_unconfigure(config): @@ -334,6 +332,7 @@ # Note that this can only be called once per testing process. assert not hasattr(self, 'args'), ( "can only parse cmdline args at most once per Config object") + self._origargs = args self._preparse(args) self._parser.hints.extend(self.pluginmanager._hints) args = self._parser.parse_setoption(args, self.option) --- a/_pytest/core.py Thu Jul 14 11:46:32 2011 -0500 +++ b/_pytest/core.py Thu Jul 14 19:11:50 2011 +0200 @@ -16,11 +16,10 @@ "junitxml resultlog doctest").split() class TagTracer: - def __init__(self, prefix="[pytest] "): + def __init__(self): self._tag2proc = {} self.writer = None self.indent = 0 - self.prefix = prefix def get(self, name): return TagTracerSub(self, (name,)) @@ -30,7 +29,7 @@ if args: indent = " " * self.indent content = " ".join(map(str, args)) - self.writer("%s%s%s\n" %(self.prefix, indent, content)) + self.writer("%s%s [%s]\n" %(indent, content, ":".join(tags))) try: self._tag2proc[tags](tags, args) except KeyError: --- a/_pytest/helpconfig.py Thu Jul 14 11:46:32 2011 -0500 +++ b/_pytest/helpconfig.py Thu Jul 14 19:11:50 2011 +0200 @@ -1,7 +1,7 @@ """ version info, help messages, tracing configuration. """ import py import pytest -import inspect, sys +import os, inspect, sys from _pytest.core import varnames def pytest_addoption(parser): @@ -18,7 +18,29 @@ help="trace considerations of conftest.py files."), group.addoption('--debug', action="store_true", dest="debug", default=False, - help="generate and show internal debugging information.") + help="store internal tracing debug information in 'pytestdebug.log'.") + + +def pytest_cmdline_parse(__multicall__): + config = __multicall__.execute() + if config.option.debug: + path = os.path.abspath("pytestdebug.log") + f = open(path, 'w') + config._debugfile = f + f.write("versions pytest-%s, py-%s, python-%s\ncwd=%s\nargs=%s\n\n" %( + pytest.__version__, py.__version__, ".".join(map(str, sys.version_info)), + os.getcwd(), config._origargs)) + config.trace.root.setwriter(f.write) + sys.stderr.write("writing pytestdebug information to %s\n" % path) + return config + + at pytest.mark.trylast +def pytest_unconfigure(config): + if hasattr(config, '_debugfile'): + config._debugfile.close() + sys.stderr.write("wrote pytestdebug information to %s\n" % + config._debugfile.name) + config.trace.root.setwriter(None) def pytest_cmdline_main(config): --- a/testing/test_config.py Thu Jul 14 11:46:32 2011 -0500 +++ b/testing/test_config.py Thu Jul 14 19:11:50 2011 +0200 @@ -88,7 +88,7 @@ config.trace.root.setwriter(l.append) config.trace("hello") assert len(l) == 1 - assert l[0] == "[pytest] hello\n" + assert l[0] == "hello [config]\n" def test_config_getvalue_honours_conftest(self, testdir): testdir.makepyfile(conftest="x=1") --- a/testing/test_core.py Thu Jul 14 11:46:32 2011 -0500 +++ b/testing/test_core.py Thu Jul 14 19:11:50 2011 +0200 @@ -586,17 +586,17 @@ class TestTracer: def test_simple(self): from _pytest.core import TagTracer - rootlogger = TagTracer("[my] ") + rootlogger = TagTracer() log = rootlogger.get("pytest") log("hello") l = [] rootlogger.setwriter(l.append) log("world") assert len(l) == 1 - assert l[0] == "[my] world\n" + assert l[0] == "world [pytest]\n" sublog = log.get("collection") sublog("hello") - assert l[1] == "[my] hello\n" + assert l[1] == "hello [pytest:collection]\n" def test_indent(self): from _pytest.core import TagTracer @@ -616,7 +616,7 @@ log.root.indent -= 1 log("last") assert len(l) == 7 - names = [x.rstrip()[len(rootlogger.prefix):] for x in l] + names = [x[:x.rfind(' [')] for x in l] assert names == ['hello', ' line1', ' line2', ' line3', ' line4', ' line5', 'last'] --- a/testing/test_helpconfig.py Thu Jul 14 11:46:32 2011 -0500 +++ b/testing/test_helpconfig.py Thu Jul 14 19:11:50 2011 +0200 @@ -62,3 +62,17 @@ "*using*pytest*py*", "*active plugins*", ]) + +def test_debug(testdir, monkeypatch): + result = testdir.runpytest("--debug") + assert result.ret == 0 + p = testdir.tmpdir.join("pytestdebug.log") + assert "pytest_sessionstart" in p.read() + +def test_PYTEST_DEBUG(testdir, monkeypatch): + monkeypatch.setenv("PYTEST_DEBUG", "1") + result = testdir.runpytest() + assert result.ret == 0 + result.stderr.fnmatch_lines([ + "*registered*PluginManager*" + ]) --- a/testing/test_terminal.py Thu Jul 14 11:46:32 2011 -0500 +++ b/testing/test_terminal.py Thu Jul 14 19:11:50 2011 +0200 @@ -524,21 +524,6 @@ ]) assert result.ret == 0 -def test_debug(testdir, monkeypatch): - result = testdir.runpytest("--debug") - result.stderr.fnmatch_lines([ - "*pytest_sessionstart*session*", - ]) - assert result.ret == 0 - -def test_PYTEST_DEBUG(testdir, monkeypatch): - monkeypatch.setenv("PYTEST_DEBUG", "1") - result = testdir.runpytest() - assert result.ret == 0 - result.stderr.fnmatch_lines([ - "*registered*PluginManager*" - ]) - class TestGenericReporting: """ this test class can be subclassed with a different option Repository URL: https://bitbucket.org/hpk42/pytest/ -- 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 Jul 14 19:17:28 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Thu, 14 Jul 2011 17:17:28 -0000 Subject: [py-svn] commit/pytest: hpk42: add some debugging tracing to assertion rewriting to understand where failures (specifically issue60) come from. Message-ID: <20110714171728.25351.51305@bitbucket02.managed.contegix.com> 1 new changeset in pytest: http://bitbucket.org/hpk42/pytest/changeset/1d023821c041/ changeset: 1d023821c041 user: hpk42 date: 2011-07-14 19:17:17 summary: add some debugging tracing to assertion rewriting to understand where failures (specifically issue60) come from. affected #: 3 files (419 bytes) --- a/_pytest/__init__.py Thu Jul 14 19:11:50 2011 +0200 +++ b/_pytest/__init__.py Thu Jul 14 19:17:17 2011 +0200 @@ -1,2 +1,2 @@ # -__version__ = '2.1.1.dev3' +__version__ = '2.1.1.dev4' --- a/_pytest/assertion/rewrite.py Thu Jul 14 19:11:50 2011 +0200 +++ b/_pytest/assertion/rewrite.py Thu Jul 14 19:17:17 2011 +0200 @@ -44,6 +44,7 @@ return None sess = self.session state = sess.config._assertstate + state.trace("find_module called for: %s" % name) names = name.rsplit(".", 1) lastname = names[-1] pth = None @@ -76,11 +77,14 @@ try: for pat in self.fnpats: if fn_pypath.fnmatch(pat): + state.trace("matched test file %r" % (fn,)) break else: return None finally: self.session = sess + else: + state.trace("matched test file (was specified on cmdline): %r" % (fn,)) # The requested module looks like a test file, so rewrite it. This is # the most magical part of the process: load the source, rewrite the # asserts, and load the rewritten source. We also cache the rewritten @@ -97,6 +101,11 @@ except py.error.EACCES: state.trace("read only directory: %r" % (fn_pypath.dirname,)) write = False + except py.error.EEXIST: + state.trace("failure to create directory: %r" % ( + fn_pypath.dirname,)) + raise + #write = False cache_name = fn_pypath.basename[:-3] + "." + PYTEST_TAG + ".pyc" pyc = os.path.join(cache_dir, cache_name) # Notice that even if we're in a read-only directory, I'm going to check --- a/setup.py Thu Jul 14 19:11:50 2011 +0200 +++ b/setup.py Thu Jul 14 19:17:17 2011 +0200 @@ -24,7 +24,7 @@ name='pytest', description='py.test: simple powerful testing with Python', long_description = long_description, - version='2.1.1.dev3', + version='2.1.1.dev4', url='http://pytest.org', license='MIT license', platforms=['unix', 'linux', 'osx', 'cygwin', 'win32'], Repository URL: https://bitbucket.org/hpk42/pytest/ -- 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 Jul 14 23:16:49 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Thu, 14 Jul 2011 21:16:49 -0000 Subject: [py-svn] commit/pytest: hpk42: add ability to build pytest man page with "cd doc ; make man". Is to be Message-ID: <20110714211649.12613.86863@bitbucket01.managed.contegix.com> 1 new changeset in pytest: http://bitbucket.org/hpk42/pytest/changeset/64b9c1463b76/ changeset: 64b9c1463b76 user: hpk42 date: 2011-07-14 23:13:32 summary: add ability to build pytest man page with "cd doc ; make man". Is to be found in doc/_build/man/pytest.1 afterwards. also streamline PDF generation a bit. affected #: 3 files (143 bytes) --- a/CHANGELOG Thu Jul 14 19:17:17 2011 +0200 +++ b/CHANGELOG Thu Jul 14 23:13:32 2011 +0200 @@ -6,6 +6,7 @@ - fix assertion rewriting in read-only directories - fix issue59: provide system-out/err tags for junitxml output - fix assertion rewriting on boolean operations with 3 or more operands +- you can now build a man page with "cd doc ; make man" Changes between 2.0.3 and 2.1.0.DEV ---------------------------------------------- --- a/doc/conf.py Thu Jul 14 19:17:17 2011 +0200 +++ b/doc/conf.py Thu Jul 14 23:13:32 2011 +0200 @@ -66,7 +66,10 @@ # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. -exclude_patterns = ['links.inc', '_build', 'test', ] # XXX +exclude_patterns = ['links.inc', '_build', 'naming20.txt', 'test/*', + 'example/attic.txt', + ] + # The reST default role (used for this markup: `text`) to use for all documents. #default_role = None @@ -208,16 +211,15 @@ #latex_appendices = [] # If false, no module index is generated. -#latex_domain_indices = True - +latex_domain_indices = False # -- Options for manual page output -------------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - ('index', 'pytest', u'pytest Documentation', - [u'holger krekel et alii'], 1) + ('usage', 'pytest', u'pytest usage', + [u'holger krekel at merlinux eu'], 1) ] @@ -225,8 +227,8 @@ # Bibliographic Dublin Core info. epub_title = u'pytest' -epub_author = u'holger krekel et alii' -epub_publisher = u'holger krekel et alii' +epub_author = u'holger krekel at merlinux eu' +epub_publisher = u'holger krekel at merlinux eu' epub_copyright = u'2011, holger krekel et alii' # The language of the text. It defaults to the language option --- a/doc/contents.txt Thu Jul 14 19:17:17 2011 +0200 +++ b/doc/contents.txt Thu Jul 14 23:13:32 2011 +0200 @@ -23,6 +23,4 @@ :hidden: changelog.txt - naming20.txt - example/attic Repository URL: https://bitbucket.org/hpk42/pytest/ -- 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 Jul 20 04:42:04 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Wed, 20 Jul 2011 02:42:04 -0000 Subject: [py-svn] commit/pytest: gutworth: escape '%' in specialized comparison explanations (fixes #63) Message-ID: <20110720024204.24855.28463@bitbucket01.managed.contegix.com> 1 new changeset in pytest: http://bitbucket.org/hpk42/pytest/changeset/3f58320421ee/ changeset: 3f58320421ee user: gutworth date: 2011-07-20 04:42:00 summary: escape '%' in specialized comparison explanations (fixes #63) affected #: 3 files (562 bytes) --- a/CHANGELOG Thu Jul 14 23:13:32 2011 +0200 +++ b/CHANGELOG Tue Jul 19 21:42:00 2011 -0500 @@ -1,6 +1,7 @@ Changes between 2.1.0 and 2.1.1.DEV ---------------------------------------------- +- fix assertion rewriting on inserts involving strings containing '%' - fix assertion rewriting on calls with a ** arg - don't cache rewritten modules if bytecode generation is disabled - fix assertion rewriting in read-only directories --- a/_pytest/assertion/__init__.py Thu Jul 14 23:13:32 2011 +0200 +++ b/_pytest/assertion/__init__.py Tue Jul 19 21:42:00 2011 -0500 @@ -45,7 +45,13 @@ config=config, op=op, left=left, right=right) for new_expl in hook_result: if new_expl: - return '\n~'.join(new_expl) + res = '\n~'.join(new_expl) + if mode == "rewrite": + # The result will be fed back a python % formatting + # operation, which will fail if there are extraneous + # '%'s in the string. Escape them here. + res = res.replace("%", "%%") + return res m = monkeypatch() config._cleanup.append(m.undo) m.setattr(py.builtin.builtins, 'AssertionError', --- a/testing/test_assertrewrite.py Thu Jul 14 23:13:32 2011 +0200 +++ b/testing/test_assertrewrite.py Tue Jul 19 21:42:00 2011 -0500 @@ -275,6 +275,11 @@ assert myany(A() < 0) assert "< 0" in getmsg(f) + def test_formatchar(self): + def f(): + assert "%test" == "test" + assert getmsg(f).startswith("assert '%test' == 'test'") + class TestRewriteOnImport: Repository URL: https://bitbucket.org/hpk42/pytest/ -- 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 Jul 20 05:42:00 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Wed, 20 Jul 2011 03:42:00 -0000 Subject: [py-svn] commit/pytest: gutworth: add a newline for window's sake Message-ID: <20110720034200.12392.35995@bitbucket01.managed.contegix.com> 1 new changeset in pytest: http://bitbucket.org/hpk42/pytest/changeset/315a9027de6d/ changeset: 315a9027de6d user: gutworth date: 2011-07-20 05:41:58 summary: add a newline for window's sake affected #: 1 file (1 byte) --- a/testing/test_assertrewrite.py Tue Jul 19 21:42:00 2011 -0500 +++ b/testing/test_assertrewrite.py Tue Jul 19 22:41:58 2011 -0500 @@ -287,7 +287,8 @@ sub = testdir.mkdir("testing") sub.join("test_readonly.py").write(""" def test_rewritten(): - assert "@py_builtins" in globals()""") + assert "@py_builtins" in globals() +""") sub.chmod(320) assert testdir.runpytest().ret == 0 Repository URL: https://bitbucket.org/hpk42/pytest/ -- 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 Jul 20 05:45:29 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Wed, 20 Jul 2011 03:45:29 -0000 Subject: [py-svn] commit/pytest: gutworth: use binary mode Message-ID: <20110720034529.12394.63574@bitbucket01.managed.contegix.com> 1 new changeset in pytest: http://bitbucket.org/hpk42/pytest/changeset/b1d57e5d8c04/ changeset: b1d57e5d8c04 user: gutworth date: 2011-07-20 05:45:27 summary: use binary mode affected #: 1 file (6 bytes) --- a/testing/test_assertrewrite.py Tue Jul 19 22:41:58 2011 -0500 +++ b/testing/test_assertrewrite.py Tue Jul 19 22:45:27 2011 -0500 @@ -288,7 +288,7 @@ sub.join("test_readonly.py").write(""" def test_rewritten(): assert "@py_builtins" in globals() -""") +""", "wb") sub.chmod(320) assert testdir.runpytest().ret == 0 Repository URL: https://bitbucket.org/hpk42/pytest/ -- 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 Jul 20 05:56:37 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Wed, 20 Jul 2011 03:56:37 -0000 Subject: [py-svn] commit/pytest: gutworth: now fix py3... Message-ID: <20110720035637.17353.4944@bitbucket02.managed.contegix.com> 1 new changeset in pytest: http://bitbucket.org/hpk42/pytest/changeset/a559cbf91592/ changeset: a559cbf91592 user: gutworth date: 2011-07-20 05:56:34 summary: now fix py3... affected #: 1 file (45 bytes) --- a/testing/test_assertrewrite.py Tue Jul 19 22:45:27 2011 -0500 +++ b/testing/test_assertrewrite.py Tue Jul 19 22:56:34 2011 -0500 @@ -285,10 +285,11 @@ def test_readonly(self, testdir): sub = testdir.mkdir("testing") - sub.join("test_readonly.py").write(""" + sub.join("test_readonly.py").write( + py.builtin._totext(""" def test_rewritten(): assert "@py_builtins" in globals() -""", "wb") +""").encode("utf-8"), "wb") sub.chmod(320) assert testdir.runpytest().ret == 0 Repository URL: https://bitbucket.org/hpk42/pytest/ -- 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 Tue Jul 26 04:40:27 2011 From: commits-noreply at bitbucket.org (Bitbucket) Date: Tue, 26 Jul 2011 02:40:27 -0000 Subject: [py-svn] commit/pytest: gutworth: only try to create the __pycache__ dir (not a tree to it) fixes #60 Message-ID: <20110726024027.17352.16182@bitbucket02.managed.contegix.com> 1 new changeset in pytest: http://bitbucket.org/hpk42/pytest/changeset/b57145c22eda/ changeset: b57145c22eda user: gutworth date: 2011-07-26 04:40:38 summary: only try to create the __pycache__ dir (not a tree to it) fixes #60 Also, improve error handling surrounding __pycache__ creation. affected #: 3 files (1.5 KB) --- a/CHANGELOG Tue Jul 19 22:56:34 2011 -0500 +++ b/CHANGELOG Mon Jul 25 21:40:38 2011 -0500 @@ -1,6 +1,7 @@ Changes between 2.1.0 and 2.1.1.DEV ---------------------------------------------- +- fix error conditions involving the creation of __pycache__ - fix assertion rewriting on inserts involving strings containing '%' - fix assertion rewriting on calls with a ** arg - don't cache rewritten modules if bytecode generation is disabled --- a/_pytest/assertion/rewrite.py Tue Jul 19 22:56:34 2011 -0500 +++ b/_pytest/assertion/rewrite.py Mon Jul 25 21:40:38 2011 -0500 @@ -2,6 +2,7 @@ import ast import collections +import errno import itertools import imp import marshal @@ -97,15 +98,23 @@ cache_dir = os.path.join(fn_pypath.dirname, "__pycache__") if write: try: - py.path.local(cache_dir).ensure(dir=True) - except py.error.EACCES: - state.trace("read only directory: %r" % (fn_pypath.dirname,)) - write = False - except py.error.EEXIST: - state.trace("failure to create directory: %r" % ( - fn_pypath.dirname,)) - raise - #write = False + os.mkdir(cache_dir) + except OSError: + e = sys.exc_info()[1].errno + if e == errno.EEXIST: + # Either the __pycache__ directory already exists (the + # common case) or it's blocked by a non-dir node. In the + # latter case, we'll ignore it in _write_pyc. + pass + elif e == errno.ENOTDIR: + # One of the path components was not a directory, likely + # because we're in a zip file. + write = False + elif e == errno.EACCES: + state.trace("read only directory: %r" % (fn_pypath.dirname,)) + write = False + else: + raise cache_name = fn_pypath.basename[:-3] + "." + PYTEST_TAG + ".pyc" pyc = os.path.join(cache_dir, cache_name) # Notice that even if we're in a read-only directory, I'm going to check @@ -146,13 +155,21 @@ # little reason deviate, and I hope sometime to be able to use # imp.load_compiled to load them. (See the comment in load_module above.) mtime = int(source_path.mtime()) - fp = open(pyc, "wb") + try: + fp = open(pyc, "wb") + except IOError: + if sys.exc_info()[1].errno == errno.ENOTDIR: + # This happens when we get a EEXIST in find_module creating the + # __pycache__ directory and __pycache__ is by some non-dir node. + return False + raise try: fp.write(imp.get_magic()) fp.write(struct.pack(" 1 new changeset in pytest: http://bitbucket.org/hpk42/pytest/changeset/603842e71e56/ changeset: 603842e71e56 user: gutworth date: 2011-07-26 04:42:57 summary: _make_rewritten_pyc doesn't need to return anything affected #: 1 file (14 bytes) --- a/_pytest/assertion/rewrite.py Mon Jul 25 21:40:38 2011 -0500 +++ b/_pytest/assertion/rewrite.py Mon Jul 25 21:42:57 2011 -0500 @@ -205,7 +205,6 @@ proc_pyc = pyc + "." + str(os.getpid()) if _write_pyc(co, fn, proc_pyc): os.rename(proc_pyc, pyc) - return co def _read_pyc(source, pyc): """Possibly read a py.test pyc containing rewritten code. Repository URL: https://bitbucket.org/hpk42/pytest/ -- This is a commit notification from bitbucket.org. You are receiving this because you have the service enabled, addressing the recipient of this email.