* 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:
parent
e391662cff
commit
13932b7f4b
|
@ -114,12 +114,12 @@ initpkg(__name__,
|
|||
|
||||
# some nice slightly magic APIs
|
||||
'magic.__doc__' : ('./magic/__init__.py', '__doc__'),
|
||||
'magic.invoke' : ('./magic/invoke.py', 'invoke'),
|
||||
'magic.revoke' : ('./magic/invoke.py', 'revoke'),
|
||||
'magic.patch' : ('./magic/patch.py', 'patch'),
|
||||
'magic.revert' : ('./magic/patch.py', 'revert'),
|
||||
'magic.invoke' : ('./code/oldmagic.py', 'invoke'),
|
||||
'magic.revoke' : ('./code/oldmagic.py', 'revoke'),
|
||||
'magic.patch' : ('./code/oldmagic.py', 'patch'),
|
||||
'magic.revert' : ('./code/oldmagic.py', 'revert'),
|
||||
'magic.autopath' : ('./magic/autopath.py', 'autopath'),
|
||||
'magic.AssertionError' : ('./magic/assertion.py', 'AssertionError'),
|
||||
'magic.AssertionError' : ('./code/oldmagic2.py', 'AssertionError'),
|
||||
|
||||
# python inspection/code-generation API
|
||||
'code.__doc__' : ('./code/__init__.py', '__doc__'),
|
||||
|
@ -130,6 +130,9 @@ initpkg(__name__,
|
|||
'code.ExceptionInfo' : ('./code/code.py', 'ExceptionInfo'),
|
||||
'code.Traceback' : ('./code/code.py', 'Traceback'),
|
||||
'code.getfslineno' : ('./code/source.py', 'getfslineno'),
|
||||
'code.patch_builtins' : ('./code/code.py', 'patch_builtins'),
|
||||
'code.unpatch_builtins' : ('./code/code.py', 'unpatch_builtins'),
|
||||
'code._AssertionError' : ('./code/assertion.py', 'AssertionError'),
|
||||
|
||||
# backports and additions of builtins
|
||||
'builtin.__doc__' : ('./builtin/__init__.py', '__doc__'),
|
||||
|
|
|
@ -4,52 +4,3 @@ import py
|
|||
import sys
|
||||
import inspect
|
||||
|
||||
def test_all_resolves():
|
||||
seen = py.builtin.set([py])
|
||||
lastlength = None
|
||||
while len(seen) != lastlength:
|
||||
lastlength = len(seen)
|
||||
for item in py.builtin.frozenset(seen):
|
||||
for value in item.__dict__.values():
|
||||
if isinstance(value, type(py.test)):
|
||||
seen.add(value)
|
||||
|
||||
|
||||
class TestAPI_V0_namespace_consistence:
|
||||
def test_path_entrypoints(self):
|
||||
assert inspect.ismodule(py.path)
|
||||
assert_class('py.path', 'local')
|
||||
assert_class('py.path', 'svnwc')
|
||||
assert_class('py.path', 'svnurl')
|
||||
|
||||
def test_magic_entrypoints(self):
|
||||
assert_function('py.magic', 'invoke')
|
||||
assert_function('py.magic', 'revoke')
|
||||
assert_function('py.magic', 'patch')
|
||||
assert_function('py.magic', 'revoke')
|
||||
|
||||
def test_process_entrypoints(self):
|
||||
assert_function('py.process', 'cmdexec')
|
||||
|
||||
def XXXtest_utest_entrypoints(self):
|
||||
# XXX TOBECOMPLETED
|
||||
assert_function('py.test', 'main')
|
||||
#assert_module('std.utest', 'collect')
|
||||
|
||||
def assert_class(modpath, name):
|
||||
mod = __import__(modpath, None, None, [name])
|
||||
obj = getattr(mod, name)
|
||||
assert inspect.isclass(obj)
|
||||
|
||||
# we don't test anymore that the exported classes have
|
||||
# the exported module path and name on them.
|
||||
#fullpath = modpath + '.' + name
|
||||
#assert obj.__module__ == modpath
|
||||
#if sys.version_info >= (2,3):
|
||||
# assert obj.__name__ == name
|
||||
|
||||
def assert_function(modpath, name):
|
||||
mod = __import__(modpath, None, None, [name])
|
||||
obj = getattr(mod, name)
|
||||
assert hasattr(obj, 'func_doc')
|
||||
#assert obj.func_name == name
|
||||
|
|
|
@ -174,3 +174,14 @@ def test_autoimport():
|
|||
from py.initpkg import autoimport
|
||||
py.std.os.environ['AUTOTEST_AUTOIMPORT'] = "nonexistmodule"
|
||||
py.test.raises(ImportError, "autoimport('autotest')")
|
||||
|
||||
|
||||
def test_all_resolves():
|
||||
seen = py.builtin.set([py])
|
||||
lastlength = None
|
||||
while len(seen) != lastlength:
|
||||
lastlength = len(seen)
|
||||
for item in py.builtin.frozenset(seen):
|
||||
for value in item.__dict__.values():
|
||||
if isinstance(value, type(py.test)):
|
||||
seen.add(value)
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
from compiler import parse, ast, pycodegen
|
||||
import py
|
||||
import __builtin__, sys
|
||||
import sys, inspect
|
||||
from compiler import parse, ast, pycodegen
|
||||
import __builtin__ as cpy_builtin
|
||||
BuiltinAssertionError = cpy_builtin.AssertionError
|
||||
|
||||
passthroughex = (KeyboardInterrupt, SystemExit, MemoryError)
|
||||
|
||||
|
@ -8,10 +10,92 @@ class Failure:
|
|||
def __init__(self, node):
|
||||
self.exc, self.value, self.tb = sys.exc_info()
|
||||
self.node = node
|
||||
#import traceback
|
||||
#traceback.print_exc()
|
||||
|
||||
from py.__.magic.viewtype import View
|
||||
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."""
|
||||
|
@ -322,8 +406,6 @@ class Getattr(Interpretable):
|
|||
self.explanation = '%s\n{%s = %s\n}' % (r, r, self.explanation)
|
||||
|
||||
# == Re-interpretation of full statements ==
|
||||
import __builtin__
|
||||
BuiltinAssertionError = __builtin__.AssertionError
|
||||
|
||||
class Assert(Interpretable):
|
||||
__view__ = ast.Assert
|
||||
|
@ -390,7 +472,7 @@ def report_failure(e):
|
|||
explanation = ", in: " + explanation
|
||||
else:
|
||||
explanation = ""
|
||||
print "%s: %s%s" % (e.exc.__name__, e.value, explanation)
|
||||
sys.stdout.write("%s: %s%s\n" % (e.exc.__name__, e.value, explanation))
|
||||
|
||||
def check(s, frame=None):
|
||||
if frame is None:
|
||||
|
@ -404,11 +486,12 @@ def check(s, frame=None):
|
|||
node.eval(frame)
|
||||
except passthroughex:
|
||||
raise
|
||||
except Failure, e:
|
||||
except Failure:
|
||||
e = sys.exc_info()[1]
|
||||
report_failure(e)
|
||||
else:
|
||||
if not frame.is_true(node.result):
|
||||
print "assertion failed:", node.nice_explanation()
|
||||
sys.stderr.write("assertion failed: %s\n" % node.nice_explanation())
|
||||
|
||||
|
||||
###########################################################
|
||||
|
@ -422,7 +505,8 @@ def interpret(source, frame, should_fail=False):
|
|||
frame = py.code.Frame(frame)
|
||||
try:
|
||||
module.run(frame)
|
||||
except Failure, e:
|
||||
except Failure:
|
||||
e = sys.exc_info()[1]
|
||||
return getfailure(e)
|
||||
except passthroughex:
|
||||
raise
|
||||
|
@ -447,7 +531,7 @@ def getmsg(excinfo):
|
|||
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,)
|
||||
raise TypeError("interpret returned non-string %r" % (x,))
|
||||
return x
|
||||
|
||||
def getfailure(e):
|
||||
|
@ -469,10 +553,40 @@ def run(s, frame=None):
|
|||
module = Interpretable(parse(s, 'exec').node)
|
||||
try:
|
||||
module.run(frame)
|
||||
except Failure, e:
|
||||
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():
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
@ -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
|
|
@ -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()
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
|
@ -1,38 +0,0 @@
|
|||
import __builtin__, sys
|
||||
import py
|
||||
from py.__.magic import exprinfo
|
||||
|
||||
BuiltinAssertionError = __builtin__.AssertionError
|
||||
|
||||
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 = exprinfo.interpret(source, f, should_fail=True)
|
||||
if not self.args:
|
||||
self.args = (self.msg,)
|
||||
else:
|
||||
self.msg = None
|
||||
|
||||
def invoke():
|
||||
py.magic.patch(__builtin__, 'AssertionError', AssertionError)
|
||||
def revoke():
|
||||
py.magic.revert(__builtin__, 'AssertionError')
|
|
@ -1,24 +0,0 @@
|
|||
import py
|
||||
import __builtin__ as cpy_builtin
|
||||
|
||||
def invoke(assertion=False, compile=False):
|
||||
""" 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.
|
||||
"""
|
||||
if assertion:
|
||||
from py.__.magic import assertion
|
||||
assertion.invoke()
|
||||
if compile:
|
||||
py.magic.patch(cpy_builtin, 'compile', py.code.compile )
|
||||
|
||||
def revoke(assertion=False, compile=False):
|
||||
""" revoke previously invoked magic (see invoke())."""
|
||||
if assertion:
|
||||
from py.__.magic import assertion
|
||||
assertion.revoke()
|
||||
if compile:
|
||||
py.magic.revert(cpy_builtin, 'compile')
|
|
@ -1,26 +0,0 @@
|
|||
|
||||
patched = {}
|
||||
|
||||
def patch(namespace, name, value):
|
||||
""" 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.
|
||||
"""
|
||||
nref = (namespace, name)
|
||||
orig = getattr(namespace, name)
|
||||
patched.setdefault(nref, []).append(orig)
|
||||
setattr(namespace, name, value)
|
||||
return orig
|
||||
|
||||
def revert(namespace, name):
|
||||
""" revert to the orginal value the last patch modified.
|
||||
Raise ValueError if no such original value exists.
|
||||
"""
|
||||
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
|
|
@ -1,106 +0,0 @@
|
|||
import py
|
||||
|
||||
def setup_module(mod):
|
||||
py.magic.invoke(assertion=1)
|
||||
|
||||
def teardown_module(mod):
|
||||
py.magic.revoke(assertion=1)
|
||||
|
||||
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")
|
||||
|
|
@ -1,156 +0,0 @@
|
|||
|
||||
import sys
|
||||
import py
|
||||
from py.__.magic.exprinfo import getmsg, interpret
|
||||
|
||||
def getexcinfo(exc, obj, *args, **kwargs):
|
||||
try:
|
||||
obj(*args, **kwargs)
|
||||
except KeyboardInterrupt:
|
||||
raise
|
||||
except exc:
|
||||
return sys.exc_info()
|
||||
else:
|
||||
raise AssertionError, "%r(*%r, **%r) did not raise" %(
|
||||
obj, args, kwargs)
|
||||
|
||||
def test_assert_exprinfo():
|
||||
def g():
|
||||
a = 1
|
||||
b = 2
|
||||
assert a == b
|
||||
excinfo = getexcinfo(AssertionError, g)
|
||||
msg = getmsg(excinfo)
|
||||
assert msg == 'assert 1 == 2'
|
||||
|
||||
def test_nested_scopes():
|
||||
def g():
|
||||
a = 1
|
||||
def h():
|
||||
return a
|
||||
b = 2
|
||||
assert h() == b
|
||||
excinfo = getexcinfo(AssertionError, g)
|
||||
msg = getmsg(excinfo)
|
||||
assert msg.startswith('assert 1 == 2\n + where 1 = ')
|
||||
|
||||
def test_nested_scopes_2():
|
||||
a = 1
|
||||
def g():
|
||||
b = 2
|
||||
assert a == b
|
||||
excinfo = getexcinfo(AssertionError, g)
|
||||
msg = getmsg(excinfo)
|
||||
assert msg == 'assert 1 == 2'
|
||||
|
||||
def test_assert_func_argument_type_error():
|
||||
def f ():
|
||||
pass
|
||||
def g():
|
||||
f(1)
|
||||
excinfo = getexcinfo(TypeError, g)
|
||||
msg = getmsg(excinfo)
|
||||
assert msg.find("takes no argument") != -1
|
||||
|
||||
class A:
|
||||
def f():
|
||||
pass
|
||||
def g():
|
||||
A().f()
|
||||
excinfo = getexcinfo(TypeError, g)
|
||||
msg = getmsg(excinfo)
|
||||
assert msg.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 global_f(u=6, v=7):
|
||||
return u*v
|
||||
|
||||
def test_exprinfo_funccall():
|
||||
def g():
|
||||
assert global_f() == 43
|
||||
excinfo = getexcinfo(AssertionError, g)
|
||||
msg = getmsg(excinfo)
|
||||
assert msg == 'assert 42 == 43\n + where 42 = global_f()'
|
||||
|
||||
def test_exprinfo_funccall_keywords():
|
||||
def g():
|
||||
assert global_f(v=11) == 67
|
||||
excinfo = getexcinfo(AssertionError, g)
|
||||
msg = getmsg(excinfo)
|
||||
assert msg == 'assert 66 == 67\n + where 66 = global_f(v=11)'
|
||||
|
||||
def test_interpretable_escapes_newlines():
|
||||
class X(object):
|
||||
def __repr__(self):
|
||||
return '1\n2'
|
||||
def g():
|
||||
assert X() == 'XXX'
|
||||
|
||||
excinfo = getexcinfo(AssertionError, g)
|
||||
msg = getmsg(excinfo)
|
||||
assert msg == "assert 1\\n2 == 'XXX'\n + where 1\\n2 = <class 'py.__.magic.testing.test_exprinfo.X'>()"
|
||||
|
||||
def test_keyboard_interrupt():
|
||||
# XXX this test is slightly strange because it is not
|
||||
# clear that "interpret" should execute "raise" statements
|
||||
# ... but it apparently currently does and it's nice to
|
||||
# exercise the code because the exprinfo-machinery is
|
||||
# not much executed when all tests pass ...
|
||||
|
||||
class DummyCode:
|
||||
co_filename = 'dummy'
|
||||
co_firstlineno = 0
|
||||
co_name = 'dummy'
|
||||
class DummyFrame:
|
||||
f_globals = f_locals = {}
|
||||
f_code = DummyCode
|
||||
f_lineno = 0
|
||||
|
||||
for exstr in "SystemExit", "KeyboardInterrupt", "MemoryError":
|
||||
ex = eval(exstr)
|
||||
try:
|
||||
interpret("raise %s" % exstr, py.code.Frame(DummyFrame))
|
||||
except ex:
|
||||
pass
|
||||
else:
|
||||
raise AssertionError, "ex %s didn't pass through" %(exstr, )
|
||||
|
||||
def test_inconsistent_assert_result(testdir):
|
||||
p = testdir.makepyfile("""
|
||||
def test_func():
|
||||
def f(l=[1,0]):
|
||||
return l.pop()
|
||||
assert f()
|
||||
""")
|
||||
result = testdir.runpytest(p)
|
||||
s = result.stdout.str()
|
||||
assert s.find("re-run") != -1
|
||||
|
||||
def test_twoarg_comparison_does_not_call_nonzero():
|
||||
# this arises e.g. in numpy array comparisons
|
||||
class X(object):
|
||||
def __eq__(self, other):
|
||||
return self
|
||||
|
||||
def __nonzero__(self):
|
||||
raise ValueError
|
||||
|
||||
def all(self):
|
||||
return False
|
||||
|
||||
def f():
|
||||
a = X()
|
||||
b = X()
|
||||
assert (a == b).all()
|
||||
|
||||
excinfo = getexcinfo(AssertionError, f)
|
||||
msg = getmsg(excinfo)
|
||||
print msg
|
||||
assert "re-run" not in msg
|
||||
assert "ValueError" not in msg
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
import __builtin__ as bltin
|
||||
import py
|
||||
import inspect
|
||||
|
||||
def check_assertion():
|
||||
excinfo = py.test.raises(AssertionError, "assert 1 == 2")
|
||||
assert excinfo.exconly(tryshort=True) == "assert 1 == 2"
|
||||
|
||||
def test_invoke_assertion():
|
||||
py.magic.invoke(assertion=True)
|
||||
try:
|
||||
check_assertion()
|
||||
finally:
|
||||
py.magic.revoke(assertion=True)
|
||||
|
||||
def test_invoke_compile():
|
||||
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)
|
||||
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
from py.test import raises
|
||||
from py.magic import patch, revert
|
||||
|
||||
def test_patch_revert():
|
||||
class a:
|
||||
pass
|
||||
raises(AttributeError, "patch(a, 'i', 42)")
|
||||
|
||||
a.i = 42
|
||||
patch(a, 'i', 23)
|
||||
assert a.i == 23
|
||||
revert(a, 'i')
|
||||
assert a.i == 42
|
||||
|
||||
def test_double_patch():
|
||||
class a:
|
||||
i = 42
|
||||
assert patch(a, 'i', 2) == 42
|
||||
assert patch(a, 'i', 3) == 2
|
||||
assert a.i == 3
|
||||
assert revert(a, 'i') == 3
|
||||
assert a.i == 2
|
||||
assert revert(a, 'i') == 2
|
||||
assert a.i == 42
|
||||
|
||||
def test_valueerror():
|
||||
class a:
|
||||
i = 2
|
||||
pass
|
||||
raises(ValueError, "revert(a, 'i')")
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
from py.__.magic.viewtype import View
|
||||
|
||||
def test_class_dispatch():
|
||||
### 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_custom_class_hierarchy():
|
||||
### 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"]
|
|
@ -1,94 +0,0 @@
|
|||
"""
|
||||
The View base class for view-based programming.
|
||||
|
||||
A view of an object is an extension of this existing object.
|
||||
This is useful to *locally* add methods or even attributes to objects
|
||||
that you have obtained from elsewhere.
|
||||
"""
|
||||
from __future__ import generators
|
||||
import inspect
|
||||
|
||||
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
|
|
@ -5,7 +5,6 @@ import py
|
|||
#
|
||||
|
||||
def main(args=None):
|
||||
warn_about_missing_assertion()
|
||||
if args is None:
|
||||
args = py.std.sys.argv[1:]
|
||||
config = py.test.config
|
||||
|
@ -20,11 +19,3 @@ def main(args=None):
|
|||
py.std.sys.stderr.write("ERROR: %s\n" %(e.args[0],))
|
||||
raise SystemExit(3)
|
||||
|
||||
def warn_about_missing_assertion():
|
||||
try:
|
||||
assert False
|
||||
except AssertionError:
|
||||
pass
|
||||
else:
|
||||
py.std.warnings.warn("Assertions are turned off!"
|
||||
" (are you using python -O?)")
|
||||
|
|
|
@ -10,5 +10,5 @@ Generator = py.test.collect.Generator
|
|||
Function = py.test.collect.Function
|
||||
Instance = py.test.collect.Instance
|
||||
|
||||
pytest_plugins = "default runner capture terminal keyword xfail tmpdir execnetcleanup monkeypatch recwarn pdb pastebin unittest helpconfig nose".split()
|
||||
pytest_plugins = "default runner capture terminal keyword xfail tmpdir execnetcleanup monkeypatch recwarn pdb pastebin unittest helpconfig nose assertion".split()
|
||||
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
import py
|
||||
|
||||
def pytest_addoption(parser):
|
||||
group = parser.getgroup("debugconfig")
|
||||
group._addoption('--no-assert', action="store_true", default=False,
|
||||
dest="noassert",
|
||||
help="disable python assert expression reinterpretation."),
|
||||
|
||||
def pytest_configure(config):
|
||||
if not config.getvalue("noassert"):
|
||||
warn_about_missing_assertion()
|
||||
config._oldassertion = py.std.__builtin__.AssertionError
|
||||
py.std.__builtin__.AssertionError = py.code._AssertionError
|
||||
|
||||
def pytest_unconfigure(config):
|
||||
if hasattr(config, '_oldassertion'):
|
||||
py.std.__builtin__.AssertionError = config._oldassertion
|
||||
del config._oldassertion
|
||||
|
||||
def warn_about_missing_assertion():
|
||||
try:
|
||||
assert False
|
||||
except AssertionError:
|
||||
pass
|
||||
else:
|
||||
py.std.warnings.warn("Assertions are turned off!"
|
||||
" (are you using python -O?)")
|
||||
|
||||
def test_functional(testdir):
|
||||
testdir.makepyfile("""
|
||||
def test_hello():
|
||||
x = 3
|
||||
assert x == 4
|
||||
""")
|
||||
result = testdir.runpytest()
|
||||
assert "3 == 4" in result.stdout.str()
|
||||
result = testdir.runpytest("--no-assert")
|
||||
assert "3 == 4" not in result.stdout.str()
|
||||
|
||||
def test_traceback_failure(testdir):
|
||||
p1 = testdir.makepyfile("""
|
||||
def g():
|
||||
return 2
|
||||
def f(x):
|
||||
assert x == g()
|
||||
def test_onefails():
|
||||
f(3)
|
||||
""")
|
||||
result = testdir.runpytest(p1)
|
||||
result.stdout.fnmatch_lines([
|
||||
"*test_traceback_failure.py F",
|
||||
"====* FAILURES *====",
|
||||
"____*____",
|
||||
"",
|
||||
" def test_onefails():",
|
||||
"> f(3)",
|
||||
"",
|
||||
"*test_*.py:6: ",
|
||||
"_ _ _ *",
|
||||
#"",
|
||||
" def f(x):",
|
||||
"> assert x == g()",
|
||||
"E assert 3 == 2",
|
||||
"E + where 2 = g()",
|
||||
"",
|
||||
"*test_traceback_failure.py:4: AssertionError"
|
||||
])
|
||||
|
|
@ -564,36 +564,6 @@ class TestTerminalFunctional:
|
|||
"=* 1 passed in *.[0-9][0-9] seconds *=",
|
||||
])
|
||||
|
||||
def test_traceback_failure(self, testdir):
|
||||
p1 = testdir.makepyfile("""
|
||||
def g():
|
||||
return 2
|
||||
def f(x):
|
||||
assert x == g()
|
||||
def test_onefails():
|
||||
f(3)
|
||||
""")
|
||||
result = testdir.runpytest(p1)
|
||||
result.stdout.fnmatch_lines([
|
||||
"*test_traceback_failure.py F",
|
||||
"====* FAILURES *====",
|
||||
"____*____",
|
||||
"",
|
||||
" def test_onefails():",
|
||||
"> f(3)",
|
||||
"",
|
||||
"*test_*.py:6: ",
|
||||
"_ _ _ *",
|
||||
#"",
|
||||
" def f(x):",
|
||||
"> assert x == g()",
|
||||
"E assert 3 == 2",
|
||||
"E + where 2 = g()",
|
||||
"",
|
||||
"*test_traceback_failure.py:4: AssertionError"
|
||||
])
|
||||
|
||||
|
||||
def test_showlocals(self, testdir):
|
||||
p1 = testdir.makepyfile("""
|
||||
def test_showlocals():
|
||||
|
|
|
@ -183,21 +183,13 @@ class Module(py.test.collect.File, PyCollectorMixin):
|
|||
def setup(self):
|
||||
if getattr(self.obj, 'disabled', 0):
|
||||
py.test.skip("%r is disabled" %(self.obj,))
|
||||
if not self.config.option.nomagic:
|
||||
#print "*" * 20, "INVOKE assertion", self
|
||||
py.magic.invoke(assertion=1)
|
||||
mod = self.obj
|
||||
#self.config.pluginmanager.register(mod)
|
||||
if hasattr(mod, 'setup_module'):
|
||||
self.obj.setup_module(mod)
|
||||
|
||||
def teardown(self):
|
||||
if hasattr(self.obj, 'teardown_module'):
|
||||
self.obj.teardown_module(self.obj)
|
||||
if not self.config.option.nomagic:
|
||||
#print "*" * 20, "revoke assertion", self
|
||||
py.magic.revoke(assertion=1)
|
||||
#self.config.pluginmanager.unregister(self.obj)
|
||||
|
||||
class Class(PyCollectorMixin, py.test.collect.Collector):
|
||||
|
||||
|
|
|
@ -22,19 +22,6 @@ class TestModule:
|
|||
py.test.raises(SyntaxError, modcol.collect)
|
||||
py.test.raises(SyntaxError, modcol.run)
|
||||
|
||||
def test_module_assertion_setup(self, testdir, monkeypatch):
|
||||
modcol = testdir.getmodulecol("pass")
|
||||
from py.__.magic import assertion
|
||||
l = []
|
||||
monkeypatch.setattr(assertion, "invoke", lambda: l.append(None))
|
||||
modcol.setup()
|
||||
x = l.pop()
|
||||
assert x is None
|
||||
monkeypatch.setattr(assertion, "revoke", lambda: l.append(None))
|
||||
modcol.teardown()
|
||||
x = l.pop()
|
||||
assert x is None
|
||||
|
||||
def test_module_considers_pluginmanager_at_import(self, testdir):
|
||||
modcol = testdir.getmodulecol("pytest_plugins='xasdlkj',")
|
||||
py.test.raises(ImportError, "modcol.obj")
|
||||
|
|
Loading…
Reference in New Issue