* 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