* deprecate py.magic.invoke/revoke in favour of

the new py.code.patch_builtins, py.code.unpatch_builtins

* deprecate py.magic.patch/revert

* deprecate py.magic.AssertionError in favour of py.code._AssertionError

* introduced pytest_assertion plugin.

--HG--
branch : trunk
This commit is contained in:
holger krekel
2009-08-27 17:26:02 +02:00
parent e391662cff
commit 13932b7f4b
26 changed files with 564 additions and 696 deletions

616
py/code/assertion.py Normal file
View File

@@ -0,0 +1,616 @@
import py
import sys, inspect
from compiler import parse, ast, pycodegen
import __builtin__ as cpy_builtin
BuiltinAssertionError = cpy_builtin.AssertionError
passthroughex = (KeyboardInterrupt, SystemExit, MemoryError)
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 = '<eval>'
self.__obj__.filename = '<eval>'
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 = '<run>'
co = pycodegen.ModuleCodeGenerator(expr).getCode()
frame.exec_(co)
except passthroughex:
raise
except:
raise Failure(self)
def nice_explanation(self):
# uck! See CallFunc for where \n{ and \n} escape sequences are used
raw_lines = (self.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('}'):
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:])
else:
assert line.startswith('}')
stack.pop()
stackcnt.pop()
result[stack[-1]] += line[1:]
assert len(stack) == 1
return '\n'.join(result)
class Name(Interpretable):
__view__ = ast.Name
def is_local(self, frame):
co = compile('%r in locals() is not globals()' % self.name, '?', 'eval')
try:
return frame.is_true(frame.eval(co))
except passthroughex:
raise
except:
return False
def is_global(self, frame):
co = compile('%r in globals()' % self.name, '?', 'eval')
try:
return frame.is_true(frame.eval(co))
except passthroughex:
raise
except:
return False
def is_builtin(self, frame):
co = compile('%r not in locals() and %r not in globals()' % (
self.name, self.name), '?', 'eval')
try:
return frame.is_true(frame.eval(co))
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)
co = compile("__exprinfo_left %s __exprinfo_right" % operation,
'?', 'eval')
try:
self.result = frame.eval(co, __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,
co=compile(astpattern, '?', 'eval')):
expr = Interpretable(self.expr)
expr.eval(frame)
self.explanation = astpattern.replace('__exprinfo_expr',
expr.explanation)
try:
self.result = frame.eval(co, __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,
co=compile(astpattern, '?', 'eval')):
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(co, __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):
co = compile('isinstance(__exprinfo_value, bool)', '?', 'eval')
try:
return frame.is_true(frame.eval(co, __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 += ')'
co = compile(source, '?', 'eval')
try:
self.result = frame.eval(co, **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)
co = compile('__exprinfo_expr.%s' % self.attrname, '?', 'eval')
try:
self.result = frame.eval(co, __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
co = compile('hasattr(__exprinfo_expr, "__dict__") and '
'%r in __exprinfo_expr.__dict__' % self.attrname,
'?', 'eval')
try:
from_instance = frame.is_true(
frame.eval(co, __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 <explanation>'
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 = '<run>'
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:
import sys
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:
import sys
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)
class AssertionError(BuiltinAssertionError):
def __init__(self, *args):
BuiltinAssertionError.__init__(self, *args)
if args:
try:
self.msg = str(args[0])
except (KeyboardInterrupt, SystemExit):
raise
except:
self.msg = "<[broken __repr__] %s at %0xd>" %(
args[0].__class__, id(args[0]))
else:
f = sys._getframe(1)
try:
source = py.code.Frame(f).statement
source = str(source.deindent()).strip()
except py.error.ENOENT:
source = None
# this can also occur during reinterpretation, when the
# co_filename is set to "<run>".
if source:
self.msg = interpret(source, f, should_fail=True)
if not self.args:
self.args = (self.msg,)
else:
self.msg = None
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())")

View File

@@ -5,7 +5,9 @@ try:
except ImportError:
import reprlib as repr
from __builtin__ import repr as builtin_repr
import __builtin__ as cpy_builtin
builtin_repr = cpy_builtin.repr
class Code(object):
""" wrapper around Python code objects """
@@ -194,9 +196,9 @@ class TracebackEntry(object):
"""Reinterpret the failing statement and returns a detailed information
about what operations are performed."""
if self.exprinfo is None:
from py.__.magic import exprinfo
from py.__.code import assertion
source = str(self.statement).strip()
x = exprinfo.interpret(source, self.frame, should_fail=True)
x = assertion.interpret(source, self.frame, should_fail=True)
if not isinstance(x, str):
raise TypeError, "interpret returned non-string %r" % (x,)
self.exprinfo = x
@@ -355,7 +357,7 @@ class ExceptionInfo(object):
# ExceptionInfo-like classes may have different attributes.
if tup is None:
tup = sys.exc_info()
if exprinfo is None and isinstance(tup[1], py.magic.AssertionError):
if exprinfo is None and isinstance(tup[1], py.code._AssertionError):
exprinfo = tup[1].msg
if exprinfo and exprinfo.startswith('assert '):
self._striptext = 'AssertionError: '
@@ -371,7 +373,7 @@ class ExceptionInfo(object):
""" return the exception as a string
when 'tryshort' resolves to True, and the exception is a
py.magic.AssertionError, only the actual exception part of
py.code._AssertionError, only the actual exception part of
the exception representation is returned (so 'AssertionError: ' is
removed from the beginning)
"""
@@ -727,3 +729,24 @@ class SafeRepr(repr.Repr):
return s
safe_repr = SafeRepr().repr
oldbuiltins = {}
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(cpy_builtin.AssertionError)
cpy_builtin.AssertionError = assertion.AssertionError
if compile:
l = oldbuiltins.setdefault('compile', [])
l.append(cpy_builtin.compile)
cpy_builtin.compile = py.code.compile
def unpatch_builtins(assertion=True, compile=True):
""" remove compile and AssertionError builtins from Python builtins. """
if assertion:
cpy_builtin.AssertionError = oldbuiltins['AssertionError'].pop()
if compile:
cpy_builtin.compile = oldbuiltins['compile'].pop()

61
py/code/oldmagic.py Normal file
View File

@@ -0,0 +1,61 @@
""" deprecated module for turning on/off some features. """
import py
import __builtin__ as cpy_builtin
def invoke(assertion=False, compile=False):
""" (deprecated) invoke magic, currently you can specify:
assertion patches the builtin AssertionError to try to give
more meaningful AssertionErrors, which by means
of deploying a mini-interpreter constructs
a useful error message.
"""
py.log._apiwarn("1.1",
"py.magic.invoke() is deprecated, use py.code.patch_builtins()",
stacklevel=2,
)
py.code.patch_builtins(assertion=assertion, compile=compile)
def revoke(assertion=False, compile=False):
""" (deprecated) revoke previously invoked magic (see invoke())."""
py.log._apiwarn("1.1",
"py.magic.revoke() is deprecated, use py.code.unpatch_builtins()",
stacklevel=2,
)
py.code.unpatch_builtins(assertion=assertion, compile=compile)
patched = {}
def patch(namespace, name, value):
""" (deprecated) rebind the 'name' on the 'namespace' to the 'value',
possibly and remember the original value. Multiple
invocations to the same namespace/name pair will
remember a list of old values.
"""
py.log._apiwarn("1.1",
"py.magic.patch() is deprecated, in tests use monkeypatch funcarg.",
stacklevel=2,
)
nref = (namespace, name)
orig = getattr(namespace, name)
patched.setdefault(nref, []).append(orig)
setattr(namespace, name, value)
return orig
def revert(namespace, name):
""" (deprecated) revert to the orginal value the last patch modified.
Raise ValueError if no such original value exists.
"""
py.log._apiwarn("1.1",
"py.magic.revert() is deprecated, in tests use monkeypatch funcarg.",
stacklevel=2,
)
nref = (namespace, name)
if nref not in patched or not patched[nref]:
raise ValueError, "No original value stored for %s.%s" % nref
current = getattr(namespace, name)
orig = patched[nref].pop()
setattr(namespace, name, orig)
return current

6
py/code/oldmagic2.py Normal file
View File

@@ -0,0 +1,6 @@
import py
py.log._apiwarn("1.1", "py.magic.AssertionError is deprecated, use py.code._AssertionError", stacklevel=2)
from py.code import _AssertionError as AssertionError

View File

@@ -0,0 +1,173 @@
import py
from py.__.code.assertion import View
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:
s = str(e)
assert s.startswith('assert 2 == 3\n')
def test_assert_with_explicit_message():
try:
assert f() == 3, "hello"
except AssertionError, e:
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:
s = str(e)
assert s.startswith('assert 2 == 3\n')
def test_assert_multiline_2():
try:
assert (f() == (4,
3)[-1])
except AssertionError, e:
s = str(e)
assert s.startswith('assert 2 ==')
def test_assert_non_string_message():
class A:
def __str__(self):
return "hello"
try:
assert 0 == 1, A()
except AssertionError, e:
assert e.msg == "hello"
# These tests should both fail, but should fail nicely...
class WeirdRepr:
def __repr__(self):
return '<WeirdRepr\nsecond line>'
def bug_test_assert_repr():
v = WeirdRepr()
try:
assert v == 1
except AssertionError, e:
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:
assert e.msg.find("list") != -1
def test_assert_implicit_multiline():
try:
x = [1,2,3]
assert x != [1,
2, 3]
except AssertionError, e:
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")
class TestView:
def test_class_dispatch(self):
### Use a custom class hierarchy with existing instances
class Picklable(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(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_AssertionError(testdir):
testdir.makepyfile("""
import py
def test_hello(recwarn):
err = py.magic.AssertionError
recwarn.pop(DeprecationWarning)
assert err is py.code._AssertionError
""")
result = testdir.runpytest()
assert "1 passed" in result.stdout.str()

View File

@@ -164,5 +164,18 @@ class TestSafeRepr:
s = safe_repr(Function())
except Exception, e:
py.test.fail("saferepr failed for newstyle class")
def test_builtin_patch_unpatch(monkeypatch):
import __builtin__ as cpy_builtin
comp = cpy_builtin.compile
def mycompile(*args, **kwargs):
return comp(*args, **kwargs)
monkeypatch.setattr(cpy_builtin, 'AssertionError', None)
monkeypatch.setattr(cpy_builtin, 'compile', mycompile)
py.code.patch_builtins()
assert cpy_builtin.AssertionError
assert cpy_builtin.compile != mycompile
py.code.unpatch_builtins()
assert cpy_builtin.AssertionError is None
assert cpy_builtin.compile == mycompile

View File

@@ -577,11 +577,11 @@ raise ValueError()
x = 1
assert x == 2
""")
py.magic.invoke(assertion=True)
py.code.patch_builtins(assertion=True)
try:
excinfo = py.test.raises(AssertionError, mod.somefunc)
finally:
py.magic.revoke(assertion=True)
py.code.unpatch_builtins(assertion=True)
p = FormattedExcinfo()
reprentry = p.repr_traceback_entry(excinfo.traceback[-1], excinfo)

View File

@@ -0,0 +1,64 @@
import py
def check_assertion():
excinfo = py.test.raises(AssertionError, "assert 1 == 2")
s = excinfo.exconly(tryshort=True)
if not s == "assert 1 == 2":
raise ValueError("assertion not enabled: got %s" % s)
def test_invoke_assertion(recwarn, monkeypatch):
monkeypatch.setattr(py.std.__builtin__, 'AssertionError', None)
py.magic.invoke(assertion=True)
try:
check_assertion()
finally:
py.magic.revoke(assertion=True)
recwarn.pop(DeprecationWarning)
def test_invoke_compile(recwarn, monkeypatch):
monkeypatch.setattr(py.std.__builtin__, 'compile', None)
py.magic.invoke(compile=True)
try:
co = compile("""if 1:
def f():
return 1
\n""", '', 'exec')
d = {}
exec co in d
assert py.code.Source(d['f'])
finally:
py.magic.revoke(compile=True)
recwarn.pop(DeprecationWarning)
def test_patch_revert(recwarn):
class a:
pass
py.test.raises(AttributeError, "py.magic.patch(a, 'i', 42)")
a.i = 42
py.magic.patch(a, 'i', 23)
assert a.i == 23
recwarn.pop(DeprecationWarning)
py.magic.revert(a, 'i')
assert a.i == 42
recwarn.pop(DeprecationWarning)
def test_double_patch(recwarn):
class a:
i = 42
assert py.magic.patch(a, 'i', 2) == 42
recwarn.pop(DeprecationWarning)
assert py.magic.patch(a, 'i', 3) == 2
assert a.i == 3
assert py.magic.revert(a, 'i') == 3
recwarn.pop(DeprecationWarning)
assert a.i == 2
assert py.magic.revert(a, 'i') == 2
assert a.i == 42
def test_valueerror(recwarn):
class a:
i = 2
pass
py.test.raises(ValueError, "py.magic.revert(a, 'i')")
recwarn.pop(DeprecationWarning)