From 8675cf640d1a8429fca069f859e85f5d158c6303 Mon Sep 17 00:00:00 2001 From: Benjamin Peterson Date: Tue, 30 Aug 2011 10:34:21 -0400 Subject: [PATCH] every boolop operand must have it's own format context (fixes #69) --- CHANGELOG | 1 + _pytest/assertion/rewrite.py | 25 ++++++++++++------------- testing/test_assertrewrite.py | 8 ++++++++ 3 files changed, 21 insertions(+), 13 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 614169950..09317c8f5 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,7 @@ Changes between 2.1.1 and [NEXT VERSION] ---------------------------------------- +- fix issue69 / assertion rewriting fixed on some boolean operations - fix issue68 / packages now work with assertion rewriting - fix issue66: use different assertion rewriting caches when the -O option is passed diff --git a/_pytest/assertion/rewrite.py b/_pytest/assertion/rewrite.py index 6a8bd1297..0a8e59e2b 100644 --- a/_pytest/assertion/rewrite.py +++ b/_pytest/assertion/rewrite.py @@ -403,13 +403,6 @@ class AssertionRewriter(ast.NodeVisitor): self.explanation_specifiers[specifier] = expr return "%(" + specifier + ")s" - def enter_cond(self, cond, body): - self.statements.append(ast.If(cond, body, [])) - self.cond_chain += cond, - - def leave_cond(self, n=1): - self.cond_chain = self.cond_chain[:-n] - def push_format_context(self): self.explanation_specifiers = {} self.stack.append(self.explanation_specifiers) @@ -484,24 +477,30 @@ class AssertionRewriter(ast.NodeVisitor): app = ast.Attribute(expl_list, "append", ast.Load()) is_or = isinstance(boolop.op, ast.Or) body = save = self.statements + fail_save = self.on_failure levels = len(boolop.values) - 1 self.push_format_context() # Process each operand, short-circuting if needed. for i, v in enumerate(boolop.values): + self.push_format_context() res, expl = self.visit(v) body.append(ast.Assign([ast.Name(res_var, ast.Store())], res)) - call = ast.Call(app, [ast.Str(expl)], [], None, None) - body.append(ast.Expr(call)) + if i: + fail_inner = [] + self.on_failure.append(ast.If(cond, fail_inner, [])) + self.on_failure = fail_inner + expl_format = self.pop_format_context(ast.Str(expl)) + call = ast.Call(app, [expl_format], [], None, None) + self.on_failure.append(ast.Expr(call)) if i < levels: - inner = [] cond = res if is_or: cond = ast.UnaryOp(ast.Not(), cond) - self.enter_cond(cond, inner) + inner = [] + self.statements.append(ast.If(cond, inner, [])) self.statements = body = inner - # Leave all conditions. - self.leave_cond(levels) self.statements = save + self.on_failure = fail_save expl_template = self.helper("format_boolop", expl_list, ast.Num(is_or)) expl = self.pop_format_context(expl_template) return ast.Name(res_var, ast.Load()), self.explanation_param(expl) diff --git a/testing/test_assertrewrite.py b/testing/test_assertrewrite.py index bf5ea5179..8d5045105 100644 --- a/testing/test_assertrewrite.py +++ b/testing/test_assertrewrite.py @@ -133,6 +133,14 @@ class TestAssertionRewrite: f = g = False assert not f and not g getmsg(f, must_pass=True) + def x(): + return False + def f(): + assert x() and x() + assert getmsg(f, {"x" : x}) == "assert (x())" + def f(): + assert False or x() + assert getmsg(f, {"x" : x}) == "assert (False or x())" def f(): f = True g = False