* 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
|
# some nice slightly magic APIs
|
||||||
'magic.__doc__' : ('./magic/__init__.py', '__doc__'),
|
'magic.__doc__' : ('./magic/__init__.py', '__doc__'),
|
||||||
'magic.invoke' : ('./magic/invoke.py', 'invoke'),
|
'magic.invoke' : ('./code/oldmagic.py', 'invoke'),
|
||||||
'magic.revoke' : ('./magic/invoke.py', 'revoke'),
|
'magic.revoke' : ('./code/oldmagic.py', 'revoke'),
|
||||||
'magic.patch' : ('./magic/patch.py', 'patch'),
|
'magic.patch' : ('./code/oldmagic.py', 'patch'),
|
||||||
'magic.revert' : ('./magic/patch.py', 'revert'),
|
'magic.revert' : ('./code/oldmagic.py', 'revert'),
|
||||||
'magic.autopath' : ('./magic/autopath.py', 'autopath'),
|
'magic.autopath' : ('./magic/autopath.py', 'autopath'),
|
||||||
'magic.AssertionError' : ('./magic/assertion.py', 'AssertionError'),
|
'magic.AssertionError' : ('./code/oldmagic2.py', 'AssertionError'),
|
||||||
|
|
||||||
# python inspection/code-generation API
|
# python inspection/code-generation API
|
||||||
'code.__doc__' : ('./code/__init__.py', '__doc__'),
|
'code.__doc__' : ('./code/__init__.py', '__doc__'),
|
||||||
|
@ -130,6 +130,9 @@ initpkg(__name__,
|
||||||
'code.ExceptionInfo' : ('./code/code.py', 'ExceptionInfo'),
|
'code.ExceptionInfo' : ('./code/code.py', 'ExceptionInfo'),
|
||||||
'code.Traceback' : ('./code/code.py', 'Traceback'),
|
'code.Traceback' : ('./code/code.py', 'Traceback'),
|
||||||
'code.getfslineno' : ('./code/source.py', 'getfslineno'),
|
'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
|
# backports and additions of builtins
|
||||||
'builtin.__doc__' : ('./builtin/__init__.py', '__doc__'),
|
'builtin.__doc__' : ('./builtin/__init__.py', '__doc__'),
|
||||||
|
|
|
@ -4,52 +4,3 @@ import py
|
||||||
import sys
|
import sys
|
||||||
import inspect
|
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
|
from py.initpkg import autoimport
|
||||||
py.std.os.environ['AUTOTEST_AUTOIMPORT'] = "nonexistmodule"
|
py.std.os.environ['AUTOTEST_AUTOIMPORT'] = "nonexistmodule"
|
||||||
py.test.raises(ImportError, "autoimport('autotest')")
|
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 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)
|
passthroughex = (KeyboardInterrupt, SystemExit, MemoryError)
|
||||||
|
|
||||||
|
@ -8,10 +10,92 @@ class Failure:
|
||||||
def __init__(self, node):
|
def __init__(self, node):
|
||||||
self.exc, self.value, self.tb = sys.exc_info()
|
self.exc, self.value, self.tb = sys.exc_info()
|
||||||
self.node = node
|
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):
|
class Interpretable(View):
|
||||||
"""A parse tree node with a few extra methods."""
|
"""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)
|
self.explanation = '%s\n{%s = %s\n}' % (r, r, self.explanation)
|
||||||
|
|
||||||
# == Re-interpretation of full statements ==
|
# == Re-interpretation of full statements ==
|
||||||
import __builtin__
|
|
||||||
BuiltinAssertionError = __builtin__.AssertionError
|
|
||||||
|
|
||||||
class Assert(Interpretable):
|
class Assert(Interpretable):
|
||||||
__view__ = ast.Assert
|
__view__ = ast.Assert
|
||||||
|
@ -390,7 +472,7 @@ def report_failure(e):
|
||||||
explanation = ", in: " + explanation
|
explanation = ", in: " + explanation
|
||||||
else:
|
else:
|
||||||
explanation = ""
|
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):
|
def check(s, frame=None):
|
||||||
if frame is None:
|
if frame is None:
|
||||||
|
@ -404,11 +486,12 @@ def check(s, frame=None):
|
||||||
node.eval(frame)
|
node.eval(frame)
|
||||||
except passthroughex:
|
except passthroughex:
|
||||||
raise
|
raise
|
||||||
except Failure, e:
|
except Failure:
|
||||||
|
e = sys.exc_info()[1]
|
||||||
report_failure(e)
|
report_failure(e)
|
||||||
else:
|
else:
|
||||||
if not frame.is_true(node.result):
|
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)
|
frame = py.code.Frame(frame)
|
||||||
try:
|
try:
|
||||||
module.run(frame)
|
module.run(frame)
|
||||||
except Failure, e:
|
except Failure:
|
||||||
|
e = sys.exc_info()[1]
|
||||||
return getfailure(e)
|
return getfailure(e)
|
||||||
except passthroughex:
|
except passthroughex:
|
||||||
raise
|
raise
|
||||||
|
@ -447,7 +531,7 @@ def getmsg(excinfo):
|
||||||
source = str(tb.statement).strip()
|
source = str(tb.statement).strip()
|
||||||
x = interpret(source, tb.frame, should_fail=True)
|
x = interpret(source, tb.frame, should_fail=True)
|
||||||
if not isinstance(x, str):
|
if not isinstance(x, str):
|
||||||
raise TypeError, "interpret returned non-string %r" % (x,)
|
raise TypeError("interpret returned non-string %r" % (x,))
|
||||||
return x
|
return x
|
||||||
|
|
||||||
def getfailure(e):
|
def getfailure(e):
|
||||||
|
@ -469,10 +553,40 @@ def run(s, frame=None):
|
||||||
module = Interpretable(parse(s, 'exec').node)
|
module = Interpretable(parse(s, 'exec').node)
|
||||||
try:
|
try:
|
||||||
module.run(frame)
|
module.run(frame)
|
||||||
except Failure, e:
|
except Failure:
|
||||||
|
e = sys.exc_info()[1]
|
||||||
report_failure(e)
|
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__':
|
if __name__ == '__main__':
|
||||||
# example:
|
# example:
|
||||||
def f():
|
def f():
|
|
@ -5,7 +5,9 @@ try:
|
||||||
except ImportError:
|
except ImportError:
|
||||||
import reprlib as repr
|
import reprlib as repr
|
||||||
|
|
||||||
from __builtin__ import repr as builtin_repr
|
import __builtin__ as cpy_builtin
|
||||||
|
|
||||||
|
builtin_repr = cpy_builtin.repr
|
||||||
|
|
||||||
class Code(object):
|
class Code(object):
|
||||||
""" wrapper around Python code objects """
|
""" wrapper around Python code objects """
|
||||||
|
@ -194,9 +196,9 @@ class TracebackEntry(object):
|
||||||
"""Reinterpret the failing statement and returns a detailed information
|
"""Reinterpret the failing statement and returns a detailed information
|
||||||
about what operations are performed."""
|
about what operations are performed."""
|
||||||
if self.exprinfo is None:
|
if self.exprinfo is None:
|
||||||
from py.__.magic import exprinfo
|
from py.__.code import assertion
|
||||||
source = str(self.statement).strip()
|
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):
|
if not isinstance(x, str):
|
||||||
raise TypeError, "interpret returned non-string %r" % (x,)
|
raise TypeError, "interpret returned non-string %r" % (x,)
|
||||||
self.exprinfo = x
|
self.exprinfo = x
|
||||||
|
@ -355,7 +357,7 @@ class ExceptionInfo(object):
|
||||||
# ExceptionInfo-like classes may have different attributes.
|
# ExceptionInfo-like classes may have different attributes.
|
||||||
if tup is None:
|
if tup is None:
|
||||||
tup = sys.exc_info()
|
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
|
exprinfo = tup[1].msg
|
||||||
if exprinfo and exprinfo.startswith('assert '):
|
if exprinfo and exprinfo.startswith('assert '):
|
||||||
self._striptext = 'AssertionError: '
|
self._striptext = 'AssertionError: '
|
||||||
|
@ -371,7 +373,7 @@ class ExceptionInfo(object):
|
||||||
""" return the exception as a string
|
""" return the exception as a string
|
||||||
|
|
||||||
when 'tryshort' resolves to True, and the exception is a
|
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
|
the exception representation is returned (so 'AssertionError: ' is
|
||||||
removed from the beginning)
|
removed from the beginning)
|
||||||
"""
|
"""
|
||||||
|
@ -727,3 +729,24 @@ class SafeRepr(repr.Repr):
|
||||||
return s
|
return s
|
||||||
|
|
||||||
safe_repr = SafeRepr().repr
|
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())
|
s = safe_repr(Function())
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
py.test.fail("saferepr failed for newstyle class")
|
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
|
x = 1
|
||||||
assert x == 2
|
assert x == 2
|
||||||
""")
|
""")
|
||||||
py.magic.invoke(assertion=True)
|
py.code.patch_builtins(assertion=True)
|
||||||
try:
|
try:
|
||||||
excinfo = py.test.raises(AssertionError, mod.somefunc)
|
excinfo = py.test.raises(AssertionError, mod.somefunc)
|
||||||
finally:
|
finally:
|
||||||
py.magic.revoke(assertion=True)
|
py.code.unpatch_builtins(assertion=True)
|
||||||
|
|
||||||
p = FormattedExcinfo()
|
p = FormattedExcinfo()
|
||||||
reprentry = p.repr_traceback_entry(excinfo.traceback[-1], excinfo)
|
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):
|
def main(args=None):
|
||||||
warn_about_missing_assertion()
|
|
||||||
if args is None:
|
if args is None:
|
||||||
args = py.std.sys.argv[1:]
|
args = py.std.sys.argv[1:]
|
||||||
config = py.test.config
|
config = py.test.config
|
||||||
|
@ -20,11 +19,3 @@ def main(args=None):
|
||||||
py.std.sys.stderr.write("ERROR: %s\n" %(e.args[0],))
|
py.std.sys.stderr.write("ERROR: %s\n" %(e.args[0],))
|
||||||
raise SystemExit(3)
|
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
|
Function = py.test.collect.Function
|
||||||
Instance = py.test.collect.Instance
|
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 *=",
|
"=* 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):
|
def test_showlocals(self, testdir):
|
||||||
p1 = testdir.makepyfile("""
|
p1 = testdir.makepyfile("""
|
||||||
def test_showlocals():
|
def test_showlocals():
|
||||||
|
|
|
@ -183,21 +183,13 @@ class Module(py.test.collect.File, PyCollectorMixin):
|
||||||
def setup(self):
|
def setup(self):
|
||||||
if getattr(self.obj, 'disabled', 0):
|
if getattr(self.obj, 'disabled', 0):
|
||||||
py.test.skip("%r is disabled" %(self.obj,))
|
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
|
mod = self.obj
|
||||||
#self.config.pluginmanager.register(mod)
|
|
||||||
if hasattr(mod, 'setup_module'):
|
if hasattr(mod, 'setup_module'):
|
||||||
self.obj.setup_module(mod)
|
self.obj.setup_module(mod)
|
||||||
|
|
||||||
def teardown(self):
|
def teardown(self):
|
||||||
if hasattr(self.obj, 'teardown_module'):
|
if hasattr(self.obj, 'teardown_module'):
|
||||||
self.obj.teardown_module(self.obj)
|
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):
|
class Class(PyCollectorMixin, py.test.collect.Collector):
|
||||||
|
|
||||||
|
|
|
@ -22,19 +22,6 @@ class TestModule:
|
||||||
py.test.raises(SyntaxError, modcol.collect)
|
py.test.raises(SyntaxError, modcol.collect)
|
||||||
py.test.raises(SyntaxError, modcol.run)
|
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):
|
def test_module_considers_pluginmanager_at_import(self, testdir):
|
||||||
modcol = testdir.getmodulecol("pytest_plugins='xasdlkj',")
|
modcol = testdir.getmodulecol("pytest_plugins='xasdlkj',")
|
||||||
py.test.raises(ImportError, "modcol.obj")
|
py.test.raises(ImportError, "modcol.obj")
|
||||||
|
|
Loading…
Reference in New Issue