parent
							
								
									ed3c96ee58
								
							
						
					
					
						commit
						a912d3745b
					
				|  | @ -0,0 +1,17 @@ | |||
| """ python inspection/code generation API """ | ||||
| from .assertion import AssertionError as _AssertionError  # noqa | ||||
| from .assertion import _format_explanation  # noqa | ||||
| from .assertion import _reprcompare  # noqa | ||||
| from .assertion import reinterpret as _reinterpret  # noqa | ||||
| from .assertion import reinterpret_old as _reinterpret_old  # noqa | ||||
| from .code import Code  # noqa | ||||
| from .code import ExceptionInfo  # noqa | ||||
| from .code import Frame  # noqa | ||||
| from .code import Traceback  # noqa | ||||
| from .code import getrawcode  # noqa | ||||
| from .code import patch_builtins  # noqa | ||||
| from .code import unpatch_builtins  # noqa | ||||
| from .source import Source  # noqa | ||||
| from .source import compile_ as compile  # noqa | ||||
| from .source import getfslineno  # noqa | ||||
| 
 | ||||
|  | @ -0,0 +1,339 @@ | |||
| """ | ||||
| Find intermediate evalutation results in assert statements through builtin AST. | ||||
| This should replace _assertionold.py eventually. | ||||
| """ | ||||
| 
 | ||||
| import ast | ||||
| import sys | ||||
| 
 | ||||
| from .assertion import _format_explanation, BuiltinAssertionError | ||||
| 
 | ||||
| if sys.platform.startswith("java") and sys.version_info < (2, 5, 2): | ||||
|     # See http://bugs.jython.org/issue1497 | ||||
|     _exprs = ("BoolOp", "BinOp", "UnaryOp", "Lambda", "IfExp", "Dict", | ||||
|               "ListComp", "GeneratorExp", "Yield", "Compare", "Call", | ||||
|               "Repr", "Num", "Str", "Attribute", "Subscript", "Name", | ||||
|               "List", "Tuple") | ||||
|     _stmts = ("FunctionDef", "ClassDef", "Return", "Delete", "Assign", | ||||
|               "AugAssign", "Print", "For", "While", "If", "With", "Raise", | ||||
|               "TryExcept", "TryFinally", "Assert", "Import", "ImportFrom", | ||||
|               "Exec", "Global", "Expr", "Pass", "Break", "Continue") | ||||
|     _expr_nodes = set(getattr(ast, name) for name in _exprs) | ||||
|     _stmt_nodes = set(getattr(ast, name) for name in _stmts) | ||||
|     def _is_ast_expr(node): | ||||
|         return node.__class__ in _expr_nodes | ||||
|     def _is_ast_stmt(node): | ||||
|         return node.__class__ in _stmt_nodes | ||||
| else: | ||||
|     def _is_ast_expr(node): | ||||
|         return isinstance(node, ast.expr) | ||||
|     def _is_ast_stmt(node): | ||||
|         return isinstance(node, ast.stmt) | ||||
| 
 | ||||
| 
 | ||||
| class Failure(Exception): | ||||
|     """Error found while interpreting AST.""" | ||||
| 
 | ||||
|     def __init__(self, explanation=""): | ||||
|         self.cause = sys.exc_info() | ||||
|         self.explanation = explanation | ||||
| 
 | ||||
| 
 | ||||
| def interpret(source, frame, should_fail=False): | ||||
|     mod = ast.parse(source) | ||||
|     visitor = DebugInterpreter(frame) | ||||
|     try: | ||||
|         visitor.visit(mod) | ||||
|     except Failure: | ||||
|         failure = sys.exc_info()[1] | ||||
|         return getfailure(failure) | ||||
|     if should_fail: | ||||
|         return ("(assertion failed, but when it was re-run for " | ||||
|                 "printing intermediate values, it did not fail.  Suggestions: " | ||||
|                 "compute assert expression before the assert or use --no-assert)") | ||||
| 
 | ||||
| def run(offending_line, frame=None): | ||||
|     from .code import Frame | ||||
|     if frame is None: | ||||
|         frame = Frame(sys._getframe(1)) | ||||
|     return interpret(offending_line, frame) | ||||
| 
 | ||||
| def getfailure(failure): | ||||
|     explanation = _format_explanation(failure.explanation) | ||||
|     value = failure.cause[1] | ||||
|     if str(value): | ||||
|         lines = explanation.splitlines() | ||||
|         if not lines: | ||||
|             lines.append("") | ||||
|         lines[0] += " << %s" % (value,) | ||||
|         explanation = "\n".join(lines) | ||||
|     text = "%s: %s" % (failure.cause[0].__name__, explanation) | ||||
|     if text.startswith("AssertionError: assert "): | ||||
|         text = text[16:] | ||||
|     return text | ||||
| 
 | ||||
| 
 | ||||
| operator_map = { | ||||
|     ast.BitOr : "|", | ||||
|     ast.BitXor : "^", | ||||
|     ast.BitAnd : "&", | ||||
|     ast.LShift : "<<", | ||||
|     ast.RShift : ">>", | ||||
|     ast.Add : "+", | ||||
|     ast.Sub : "-", | ||||
|     ast.Mult : "*", | ||||
|     ast.Div : "/", | ||||
|     ast.FloorDiv : "//", | ||||
|     ast.Mod : "%", | ||||
|     ast.Eq : "==", | ||||
|     ast.NotEq : "!=", | ||||
|     ast.Lt : "<", | ||||
|     ast.LtE : "<=", | ||||
|     ast.Gt : ">", | ||||
|     ast.GtE : ">=", | ||||
|     ast.Pow : "**", | ||||
|     ast.Is : "is", | ||||
|     ast.IsNot : "is not", | ||||
|     ast.In : "in", | ||||
|     ast.NotIn : "not in" | ||||
| } | ||||
| 
 | ||||
| unary_map = { | ||||
|     ast.Not : "not %s", | ||||
|     ast.Invert : "~%s", | ||||
|     ast.USub : "-%s", | ||||
|     ast.UAdd : "+%s" | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| class DebugInterpreter(ast.NodeVisitor): | ||||
|     """Interpret AST nodes to gleam useful debugging information. """ | ||||
| 
 | ||||
|     def __init__(self, frame): | ||||
|         self.frame = frame | ||||
| 
 | ||||
|     def generic_visit(self, node): | ||||
|         # Fallback when we don't have a special implementation. | ||||
|         if _is_ast_expr(node): | ||||
|             mod = ast.Expression(node) | ||||
|             co = self._compile(mod) | ||||
|             try: | ||||
|                 result = self.frame.eval(co) | ||||
|             except Exception: | ||||
|                 raise Failure() | ||||
|             explanation = self.frame.repr(result) | ||||
|             return explanation, result | ||||
|         elif _is_ast_stmt(node): | ||||
|             mod = ast.Module([node]) | ||||
|             co = self._compile(mod, "exec") | ||||
|             try: | ||||
|                 self.frame.exec_(co) | ||||
|             except Exception: | ||||
|                 raise Failure() | ||||
|             return None, None | ||||
|         else: | ||||
|             raise AssertionError("can't handle %s" %(node,)) | ||||
| 
 | ||||
|     def _compile(self, source, mode="eval"): | ||||
|         return compile(source, "<assertion interpretation>", mode) | ||||
| 
 | ||||
|     def visit_Expr(self, expr): | ||||
|         return self.visit(expr.value) | ||||
| 
 | ||||
|     def visit_Module(self, mod): | ||||
|         for stmt in mod.body: | ||||
|             self.visit(stmt) | ||||
| 
 | ||||
|     def visit_Name(self, name): | ||||
|         explanation, result = self.generic_visit(name) | ||||
|         # See if the name is local. | ||||
|         source = "%r in locals() is not globals()" % (name.id,) | ||||
|         co = self._compile(source) | ||||
|         try: | ||||
|             local = self.frame.eval(co) | ||||
|         except Exception: | ||||
|             # have to assume it isn't | ||||
|             local = False | ||||
|         if not local: | ||||
|             return name.id, result | ||||
|         return explanation, result | ||||
| 
 | ||||
|     def visit_Compare(self, comp): | ||||
|         left = comp.left | ||||
|         left_explanation, left_result = self.visit(left) | ||||
|         for op, next_op in zip(comp.ops, comp.comparators): | ||||
|             next_explanation, next_result = self.visit(next_op) | ||||
|             op_symbol = operator_map[op.__class__] | ||||
|             explanation = "%s %s %s" % (left_explanation, op_symbol, | ||||
|                                         next_explanation) | ||||
|             source = "__exprinfo_left %s __exprinfo_right" % (op_symbol,) | ||||
|             co = self._compile(source) | ||||
|             try: | ||||
|                 result = self.frame.eval(co, __exprinfo_left=left_result, | ||||
|                                          __exprinfo_right=next_result) | ||||
|             except Exception: | ||||
|                 raise Failure(explanation) | ||||
|             try: | ||||
|                 if not result: | ||||
|                     break | ||||
|             except KeyboardInterrupt: | ||||
|                 raise | ||||
|             except: | ||||
|                 break | ||||
|             left_explanation, left_result = next_explanation, next_result | ||||
| 
 | ||||
|         import _pytest._code | ||||
|         rcomp = _pytest._code._reprcompare | ||||
|         if rcomp: | ||||
|             res = rcomp(op_symbol, left_result, next_result) | ||||
|             if res: | ||||
|                 explanation = res | ||||
|         return explanation, result | ||||
| 
 | ||||
|     def visit_BoolOp(self, boolop): | ||||
|         is_or = isinstance(boolop.op, ast.Or) | ||||
|         explanations = [] | ||||
|         for operand in boolop.values: | ||||
|             explanation, result = self.visit(operand) | ||||
|             explanations.append(explanation) | ||||
|             if result == is_or: | ||||
|                 break | ||||
|         name = is_or and " or " or " and " | ||||
|         explanation = "(" + name.join(explanations) + ")" | ||||
|         return explanation, result | ||||
| 
 | ||||
|     def visit_UnaryOp(self, unary): | ||||
|         pattern = unary_map[unary.op.__class__] | ||||
|         operand_explanation, operand_result = self.visit(unary.operand) | ||||
|         explanation = pattern % (operand_explanation,) | ||||
|         co = self._compile(pattern % ("__exprinfo_expr",)) | ||||
|         try: | ||||
|             result = self.frame.eval(co, __exprinfo_expr=operand_result) | ||||
|         except Exception: | ||||
|             raise Failure(explanation) | ||||
|         return explanation, result | ||||
| 
 | ||||
|     def visit_BinOp(self, binop): | ||||
|         left_explanation, left_result = self.visit(binop.left) | ||||
|         right_explanation, right_result = self.visit(binop.right) | ||||
|         symbol = operator_map[binop.op.__class__] | ||||
|         explanation = "(%s %s %s)" % (left_explanation, symbol, | ||||
|                                       right_explanation) | ||||
|         source = "__exprinfo_left %s __exprinfo_right" % (symbol,) | ||||
|         co = self._compile(source) | ||||
|         try: | ||||
|             result = self.frame.eval(co, __exprinfo_left=left_result, | ||||
|                                      __exprinfo_right=right_result) | ||||
|         except Exception: | ||||
|             raise Failure(explanation) | ||||
|         return explanation, result | ||||
| 
 | ||||
|     def visit_Call(self, call): | ||||
|         func_explanation, func = self.visit(call.func) | ||||
|         arg_explanations = [] | ||||
|         ns = {"__exprinfo_func" : func} | ||||
|         arguments = [] | ||||
|         for arg in call.args: | ||||
|             arg_explanation, arg_result = self.visit(arg) | ||||
|             arg_name = "__exprinfo_%s" % (len(ns),) | ||||
|             ns[arg_name] = arg_result | ||||
|             arguments.append(arg_name) | ||||
|             arg_explanations.append(arg_explanation) | ||||
|         for keyword in call.keywords: | ||||
|             arg_explanation, arg_result = self.visit(keyword.value) | ||||
|             arg_name = "__exprinfo_%s" % (len(ns),) | ||||
|             ns[arg_name] = arg_result | ||||
|             keyword_source = "%s=%%s" % (keyword.arg) | ||||
|             arguments.append(keyword_source % (arg_name,)) | ||||
|             arg_explanations.append(keyword_source % (arg_explanation,)) | ||||
|         if call.starargs: | ||||
|             arg_explanation, arg_result = self.visit(call.starargs) | ||||
|             arg_name = "__exprinfo_star" | ||||
|             ns[arg_name] = arg_result | ||||
|             arguments.append("*%s" % (arg_name,)) | ||||
|             arg_explanations.append("*%s" % (arg_explanation,)) | ||||
|         if call.kwargs: | ||||
|             arg_explanation, arg_result = self.visit(call.kwargs) | ||||
|             arg_name = "__exprinfo_kwds" | ||||
|             ns[arg_name] = arg_result | ||||
|             arguments.append("**%s" % (arg_name,)) | ||||
|             arg_explanations.append("**%s" % (arg_explanation,)) | ||||
|         args_explained = ", ".join(arg_explanations) | ||||
|         explanation = "%s(%s)" % (func_explanation, args_explained) | ||||
|         args = ", ".join(arguments) | ||||
|         source = "__exprinfo_func(%s)" % (args,) | ||||
|         co = self._compile(source) | ||||
|         try: | ||||
|             result = self.frame.eval(co, **ns) | ||||
|         except Exception: | ||||
|             raise Failure(explanation) | ||||
|         pattern = "%s\n{%s = %s\n}" | ||||
|         rep = self.frame.repr(result) | ||||
|         explanation = pattern % (rep, rep, explanation) | ||||
|         return explanation, result | ||||
| 
 | ||||
|     def _is_builtin_name(self, name): | ||||
|         pattern = "%r not in globals() and %r not in locals()" | ||||
|         source = pattern % (name.id, name.id) | ||||
|         co = self._compile(source) | ||||
|         try: | ||||
|             return self.frame.eval(co) | ||||
|         except Exception: | ||||
|             return False | ||||
| 
 | ||||
|     def visit_Attribute(self, attr): | ||||
|         if not isinstance(attr.ctx, ast.Load): | ||||
|             return self.generic_visit(attr) | ||||
|         source_explanation, source_result = self.visit(attr.value) | ||||
|         explanation = "%s.%s" % (source_explanation, attr.attr) | ||||
|         source = "__exprinfo_expr.%s" % (attr.attr,) | ||||
|         co = self._compile(source) | ||||
|         try: | ||||
|             result = self.frame.eval(co, __exprinfo_expr=source_result) | ||||
|         except Exception: | ||||
|             raise Failure(explanation) | ||||
|         explanation = "%s\n{%s = %s.%s\n}" % (self.frame.repr(result), | ||||
|                                               self.frame.repr(result), | ||||
|                                               source_explanation, attr.attr) | ||||
|         # Check if the attr is from an instance. | ||||
|         source = "%r in getattr(__exprinfo_expr, '__dict__', {})" | ||||
|         source = source % (attr.attr,) | ||||
|         co = self._compile(source) | ||||
|         try: | ||||
|             from_instance = self.frame.eval(co, __exprinfo_expr=source_result) | ||||
|         except Exception: | ||||
|             from_instance = True | ||||
|         if from_instance: | ||||
|             rep = self.frame.repr(result) | ||||
|             pattern = "%s\n{%s = %s\n}" | ||||
|             explanation = pattern % (rep, rep, explanation) | ||||
|         return explanation, result | ||||
| 
 | ||||
|     def visit_Assert(self, assrt): | ||||
|         test_explanation, test_result = self.visit(assrt.test) | ||||
|         if test_explanation.startswith("False\n{False =") and \ | ||||
|                 test_explanation.endswith("\n"): | ||||
|             test_explanation = test_explanation[15:-2] | ||||
|         explanation = "assert %s" % (test_explanation,) | ||||
|         if not test_result: | ||||
|             try: | ||||
|                 raise BuiltinAssertionError | ||||
|             except Exception: | ||||
|                 raise Failure(explanation) | ||||
|         return explanation, test_result | ||||
| 
 | ||||
|     def visit_Assign(self, assign): | ||||
|         value_explanation, value_result = self.visit(assign.value) | ||||
|         explanation = "... = %s" % (value_explanation,) | ||||
|         name = ast.Name("__exprinfo_expr", ast.Load(), | ||||
|                         lineno=assign.value.lineno, | ||||
|                         col_offset=assign.value.col_offset) | ||||
|         new_assign = ast.Assign(assign.targets, name, lineno=assign.lineno, | ||||
|                                 col_offset=assign.col_offset) | ||||
|         mod = ast.Module([new_assign]) | ||||
|         co = self._compile(mod, "exec") | ||||
|         try: | ||||
|             self.frame.exec_(co, __exprinfo_expr=value_result) | ||||
|         except Exception: | ||||
|             raise Failure(explanation) | ||||
|         return explanation, value_result | ||||
|  | @ -0,0 +1,561 @@ | |||
| import inspect | ||||
| import sys | ||||
| 
 | ||||
| from compiler import parse, ast, pycodegen | ||||
| 
 | ||||
| import py | ||||
| from _pytest._code.assertion import BuiltinAssertionError, _format_explanation | ||||
| 
 | ||||
| passthroughex = py.builtin._sysex | ||||
| 
 | ||||
| class Failure: | ||||
|     def __init__(self, node): | ||||
|         self.exc, self.value, self.tb = sys.exc_info() | ||||
|         self.node = node | ||||
| 
 | ||||
| class View(object): | ||||
|     """View base class. | ||||
| 
 | ||||
|     If C is a subclass of View, then C(x) creates a proxy object around | ||||
|     the object x.  The actual class of the proxy is not C in general, | ||||
|     but a *subclass* of C determined by the rules below.  To avoid confusion | ||||
|     we call view class the class of the proxy (a subclass of C, so of View) | ||||
|     and object class the class of x. | ||||
| 
 | ||||
|     Attributes and methods not found in the proxy are automatically read on x. | ||||
|     Other operations like setting attributes are performed on the proxy, as | ||||
|     determined by its view class.  The object x is available from the proxy | ||||
|     as its __obj__ attribute. | ||||
| 
 | ||||
|     The view class selection is determined by the __view__ tuples and the | ||||
|     optional __viewkey__ method.  By default, the selected view class is the | ||||
|     most specific subclass of C whose __view__ mentions the class of x. | ||||
|     If no such subclass is found, the search proceeds with the parent | ||||
|     object classes.  For example, C(True) will first look for a subclass | ||||
|     of C with __view__ = (..., bool, ...) and only if it doesn't find any | ||||
|     look for one with __view__ = (..., int, ...), and then ..., object,... | ||||
|     If everything fails the class C itself is considered to be the default. | ||||
| 
 | ||||
|     Alternatively, the view class selection can be driven by another aspect | ||||
|     of the object x, instead of the class of x, by overriding __viewkey__. | ||||
|     See last example at the end of this module. | ||||
|     """ | ||||
| 
 | ||||
|     _viewcache = {} | ||||
|     __view__ = () | ||||
| 
 | ||||
|     def __new__(rootclass, obj, *args, **kwds): | ||||
|         self = object.__new__(rootclass) | ||||
|         self.__obj__ = obj | ||||
|         self.__rootclass__ = rootclass | ||||
|         key = self.__viewkey__() | ||||
|         try: | ||||
|             self.__class__ = self._viewcache[key] | ||||
|         except KeyError: | ||||
|             self.__class__ = self._selectsubclass(key) | ||||
|         return self | ||||
| 
 | ||||
|     def __getattr__(self, attr): | ||||
|         # attributes not found in the normal hierarchy rooted on View | ||||
|         # are looked up in the object's real class | ||||
|         return getattr(self.__obj__, attr) | ||||
| 
 | ||||
|     def __viewkey__(self): | ||||
|         return self.__obj__.__class__ | ||||
| 
 | ||||
|     def __matchkey__(self, key, subclasses): | ||||
|         if inspect.isclass(key): | ||||
|             keys = inspect.getmro(key) | ||||
|         else: | ||||
|             keys = [key] | ||||
|         for key in keys: | ||||
|             result = [C for C in subclasses if key in C.__view__] | ||||
|             if result: | ||||
|                 return result | ||||
|         return [] | ||||
| 
 | ||||
|     def _selectsubclass(self, key): | ||||
|         subclasses = list(enumsubclasses(self.__rootclass__)) | ||||
|         for C in subclasses: | ||||
|             if not isinstance(C.__view__, tuple): | ||||
|                 C.__view__ = (C.__view__,) | ||||
|         choices = self.__matchkey__(key, subclasses) | ||||
|         if not choices: | ||||
|             return self.__rootclass__ | ||||
|         elif len(choices) == 1: | ||||
|             return choices[0] | ||||
|         else: | ||||
|             # combine the multiple choices | ||||
|             return type('?', tuple(choices), {}) | ||||
| 
 | ||||
|     def __repr__(self): | ||||
|         return '%s(%r)' % (self.__rootclass__.__name__, self.__obj__) | ||||
| 
 | ||||
| 
 | ||||
| def enumsubclasses(cls): | ||||
|     for subcls in cls.__subclasses__(): | ||||
|         for subsubclass in enumsubclasses(subcls): | ||||
|             yield subsubclass | ||||
|     yield cls | ||||
| 
 | ||||
| 
 | ||||
| class Interpretable(View): | ||||
|     """A parse tree node with a few extra methods.""" | ||||
|     explanation = None | ||||
| 
 | ||||
|     def is_builtin(self, frame): | ||||
|         return False | ||||
| 
 | ||||
|     def eval(self, frame): | ||||
|         # fall-back for unknown expression nodes | ||||
|         try: | ||||
|             expr = ast.Expression(self.__obj__) | ||||
|             expr.filename = '<eval>' | ||||
|             self.__obj__.filename = '<eval>' | ||||
|             co = pycodegen.ExpressionCodeGenerator(expr).getCode() | ||||
|             result = frame.eval(co) | ||||
|         except passthroughex: | ||||
|             raise | ||||
|         except: | ||||
|             raise Failure(self) | ||||
|         self.result = result | ||||
|         self.explanation = self.explanation or frame.repr(self.result) | ||||
| 
 | ||||
|     def run(self, frame): | ||||
|         # fall-back for unknown statement nodes | ||||
|         try: | ||||
|             expr = ast.Module(None, ast.Stmt([self.__obj__])) | ||||
|             expr.filename = '<run>' | ||||
|             co = pycodegen.ModuleCodeGenerator(expr).getCode() | ||||
|             frame.exec_(co) | ||||
|         except passthroughex: | ||||
|             raise | ||||
|         except: | ||||
|             raise Failure(self) | ||||
| 
 | ||||
|     def nice_explanation(self): | ||||
|         return _format_explanation(self.explanation) | ||||
| 
 | ||||
| 
 | ||||
| class Name(Interpretable): | ||||
|     __view__ = ast.Name | ||||
| 
 | ||||
|     def is_local(self, frame): | ||||
|         source = '%r in locals() is not globals()' % self.name | ||||
|         try: | ||||
|             return frame.is_true(frame.eval(source)) | ||||
|         except passthroughex: | ||||
|             raise | ||||
|         except: | ||||
|             return False | ||||
| 
 | ||||
|     def is_global(self, frame): | ||||
|         source = '%r in globals()' % self.name | ||||
|         try: | ||||
|             return frame.is_true(frame.eval(source)) | ||||
|         except passthroughex: | ||||
|             raise | ||||
|         except: | ||||
|             return False | ||||
| 
 | ||||
|     def is_builtin(self, frame): | ||||
|         source = '%r not in locals() and %r not in globals()' % ( | ||||
|             self.name, self.name) | ||||
|         try: | ||||
|             return frame.is_true(frame.eval(source)) | ||||
|         except passthroughex: | ||||
|             raise | ||||
|         except: | ||||
|             return False | ||||
| 
 | ||||
|     def eval(self, frame): | ||||
|         super(Name, self).eval(frame) | ||||
|         if not self.is_local(frame): | ||||
|             self.explanation = self.name | ||||
| 
 | ||||
| class Compare(Interpretable): | ||||
|     __view__ = ast.Compare | ||||
| 
 | ||||
|     def eval(self, frame): | ||||
|         expr = Interpretable(self.expr) | ||||
|         expr.eval(frame) | ||||
|         for operation, expr2 in self.ops: | ||||
|             if hasattr(self, 'result'): | ||||
|                 # shortcutting in chained expressions | ||||
|                 if not frame.is_true(self.result): | ||||
|                     break | ||||
|             expr2 = Interpretable(expr2) | ||||
|             expr2.eval(frame) | ||||
|             self.explanation = "%s %s %s" % ( | ||||
|                 expr.explanation, operation, expr2.explanation) | ||||
|             source = "__exprinfo_left %s __exprinfo_right" % operation | ||||
|             try: | ||||
|                 self.result = frame.eval(source, | ||||
|                                          __exprinfo_left=expr.result, | ||||
|                                          __exprinfo_right=expr2.result) | ||||
|             except passthroughex: | ||||
|                 raise | ||||
|             except: | ||||
|                 raise Failure(self) | ||||
|             expr = expr2 | ||||
| 
 | ||||
| class And(Interpretable): | ||||
|     __view__ = ast.And | ||||
| 
 | ||||
|     def eval(self, frame): | ||||
|         explanations = [] | ||||
|         for expr in self.nodes: | ||||
|             expr = Interpretable(expr) | ||||
|             expr.eval(frame) | ||||
|             explanations.append(expr.explanation) | ||||
|             self.result = expr.result | ||||
|             if not frame.is_true(expr.result): | ||||
|                 break | ||||
|         self.explanation = '(' + ' and '.join(explanations) + ')' | ||||
| 
 | ||||
| class Or(Interpretable): | ||||
|     __view__ = ast.Or | ||||
| 
 | ||||
|     def eval(self, frame): | ||||
|         explanations = [] | ||||
|         for expr in self.nodes: | ||||
|             expr = Interpretable(expr) | ||||
|             expr.eval(frame) | ||||
|             explanations.append(expr.explanation) | ||||
|             self.result = expr.result | ||||
|             if frame.is_true(expr.result): | ||||
|                 break | ||||
|         self.explanation = '(' + ' or '.join(explanations) + ')' | ||||
| 
 | ||||
| 
 | ||||
| # == Unary operations == | ||||
| keepalive = [] | ||||
| for astclass, astpattern in { | ||||
|     ast.Not    : 'not __exprinfo_expr', | ||||
|     ast.Invert : '(~__exprinfo_expr)', | ||||
|     }.items(): | ||||
| 
 | ||||
|     class UnaryArith(Interpretable): | ||||
|         __view__ = astclass | ||||
| 
 | ||||
|         def eval(self, frame, astpattern=astpattern): | ||||
|             expr = Interpretable(self.expr) | ||||
|             expr.eval(frame) | ||||
|             self.explanation = astpattern.replace('__exprinfo_expr', | ||||
|                                                   expr.explanation) | ||||
|             try: | ||||
|                 self.result = frame.eval(astpattern, | ||||
|                                          __exprinfo_expr=expr.result) | ||||
|             except passthroughex: | ||||
|                 raise | ||||
|             except: | ||||
|                 raise Failure(self) | ||||
| 
 | ||||
|     keepalive.append(UnaryArith) | ||||
| 
 | ||||
| # == Binary operations == | ||||
| for astclass, astpattern in { | ||||
|     ast.Add    : '(__exprinfo_left + __exprinfo_right)', | ||||
|     ast.Sub    : '(__exprinfo_left - __exprinfo_right)', | ||||
|     ast.Mul    : '(__exprinfo_left * __exprinfo_right)', | ||||
|     ast.Div    : '(__exprinfo_left / __exprinfo_right)', | ||||
|     ast.Mod    : '(__exprinfo_left % __exprinfo_right)', | ||||
|     ast.Power  : '(__exprinfo_left ** __exprinfo_right)', | ||||
|     }.items(): | ||||
| 
 | ||||
|     class BinaryArith(Interpretable): | ||||
|         __view__ = astclass | ||||
| 
 | ||||
|         def eval(self, frame, astpattern=astpattern): | ||||
|             left = Interpretable(self.left) | ||||
|             left.eval(frame) | ||||
|             right = Interpretable(self.right) | ||||
|             right.eval(frame) | ||||
|             self.explanation = (astpattern | ||||
|                                 .replace('__exprinfo_left',  left .explanation) | ||||
|                                 .replace('__exprinfo_right', right.explanation)) | ||||
|             try: | ||||
|                 self.result = frame.eval(astpattern, | ||||
|                                          __exprinfo_left=left.result, | ||||
|                                          __exprinfo_right=right.result) | ||||
|             except passthroughex: | ||||
|                 raise | ||||
|             except: | ||||
|                 raise Failure(self) | ||||
| 
 | ||||
|     keepalive.append(BinaryArith) | ||||
| 
 | ||||
| 
 | ||||
| class CallFunc(Interpretable): | ||||
|     __view__ = ast.CallFunc | ||||
| 
 | ||||
|     def is_bool(self, frame): | ||||
|         source = 'isinstance(__exprinfo_value, bool)' | ||||
|         try: | ||||
|             return frame.is_true(frame.eval(source, | ||||
|                                             __exprinfo_value=self.result)) | ||||
|         except passthroughex: | ||||
|             raise | ||||
|         except: | ||||
|             return False | ||||
| 
 | ||||
|     def eval(self, frame): | ||||
|         node = Interpretable(self.node) | ||||
|         node.eval(frame) | ||||
|         explanations = [] | ||||
|         vars = {'__exprinfo_fn': node.result} | ||||
|         source = '__exprinfo_fn(' | ||||
|         for a in self.args: | ||||
|             if isinstance(a, ast.Keyword): | ||||
|                 keyword = a.name | ||||
|                 a = a.expr | ||||
|             else: | ||||
|                 keyword = None | ||||
|             a = Interpretable(a) | ||||
|             a.eval(frame) | ||||
|             argname = '__exprinfo_%d' % len(vars) | ||||
|             vars[argname] = a.result | ||||
|             if keyword is None: | ||||
|                 source += argname + ',' | ||||
|                 explanations.append(a.explanation) | ||||
|             else: | ||||
|                 source += '%s=%s,' % (keyword, argname) | ||||
|                 explanations.append('%s=%s' % (keyword, a.explanation)) | ||||
|         if self.star_args: | ||||
|             star_args = Interpretable(self.star_args) | ||||
|             star_args.eval(frame) | ||||
|             argname = '__exprinfo_star' | ||||
|             vars[argname] = star_args.result | ||||
|             source += '*' + argname + ',' | ||||
|             explanations.append('*' + star_args.explanation) | ||||
|         if self.dstar_args: | ||||
|             dstar_args = Interpretable(self.dstar_args) | ||||
|             dstar_args.eval(frame) | ||||
|             argname = '__exprinfo_kwds' | ||||
|             vars[argname] = dstar_args.result | ||||
|             source += '**' + argname + ',' | ||||
|             explanations.append('**' + dstar_args.explanation) | ||||
|         self.explanation = "%s(%s)" % ( | ||||
|             node.explanation, ', '.join(explanations)) | ||||
|         if source.endswith(','): | ||||
|             source = source[:-1] | ||||
|         source += ')' | ||||
|         try: | ||||
|             self.result = frame.eval(source, **vars) | ||||
|         except passthroughex: | ||||
|             raise | ||||
|         except: | ||||
|             raise Failure(self) | ||||
|         if not node.is_builtin(frame) or not self.is_bool(frame): | ||||
|             r = frame.repr(self.result) | ||||
|             self.explanation = '%s\n{%s = %s\n}' % (r, r, self.explanation) | ||||
| 
 | ||||
| class Getattr(Interpretable): | ||||
|     __view__ = ast.Getattr | ||||
| 
 | ||||
|     def eval(self, frame): | ||||
|         expr = Interpretable(self.expr) | ||||
|         expr.eval(frame) | ||||
|         source = '__exprinfo_expr.%s' % self.attrname | ||||
|         try: | ||||
|             self.result = frame.eval(source, __exprinfo_expr=expr.result) | ||||
|         except passthroughex: | ||||
|             raise | ||||
|         except: | ||||
|             raise Failure(self) | ||||
|         self.explanation = '%s.%s' % (expr.explanation, self.attrname) | ||||
|         # if the attribute comes from the instance, its value is interesting | ||||
|         source = ('hasattr(__exprinfo_expr, "__dict__") and ' | ||||
|                   '%r in __exprinfo_expr.__dict__' % self.attrname) | ||||
|         try: | ||||
|             from_instance = frame.is_true( | ||||
|                 frame.eval(source, __exprinfo_expr=expr.result)) | ||||
|         except passthroughex: | ||||
|             raise | ||||
|         except: | ||||
|             from_instance = True | ||||
|         if from_instance: | ||||
|             r = frame.repr(self.result) | ||||
|             self.explanation = '%s\n{%s = %s\n}' % (r, r, self.explanation) | ||||
| 
 | ||||
| # == Re-interpretation of full statements == | ||||
| 
 | ||||
| class Assert(Interpretable): | ||||
|     __view__ = ast.Assert | ||||
| 
 | ||||
|     def run(self, frame): | ||||
|         test = Interpretable(self.test) | ||||
|         test.eval(frame) | ||||
|         # simplify 'assert False where False = ...' | ||||
|         if (test.explanation.startswith('False\n{False = ') and | ||||
|             test.explanation.endswith('\n}')): | ||||
|             test.explanation = test.explanation[15:-2] | ||||
|         # print the result as  'assert <explanation>' | ||||
|         self.result = test.result | ||||
|         self.explanation = 'assert ' + test.explanation | ||||
|         if not frame.is_true(test.result): | ||||
|             try: | ||||
|                 raise BuiltinAssertionError | ||||
|             except passthroughex: | ||||
|                 raise | ||||
|             except: | ||||
|                 raise Failure(self) | ||||
| 
 | ||||
| class Assign(Interpretable): | ||||
|     __view__ = ast.Assign | ||||
| 
 | ||||
|     def run(self, frame): | ||||
|         expr = Interpretable(self.expr) | ||||
|         expr.eval(frame) | ||||
|         self.result = expr.result | ||||
|         self.explanation = '... = ' + expr.explanation | ||||
|         # fall-back-run the rest of the assignment | ||||
|         ass = ast.Assign(self.nodes, ast.Name('__exprinfo_expr')) | ||||
|         mod = ast.Module(None, ast.Stmt([ass])) | ||||
|         mod.filename = '<run>' | ||||
|         co = pycodegen.ModuleCodeGenerator(mod).getCode() | ||||
|         try: | ||||
|             frame.exec_(co, __exprinfo_expr=expr.result) | ||||
|         except passthroughex: | ||||
|             raise | ||||
|         except: | ||||
|             raise Failure(self) | ||||
| 
 | ||||
| class Discard(Interpretable): | ||||
|     __view__ = ast.Discard | ||||
| 
 | ||||
|     def run(self, frame): | ||||
|         expr = Interpretable(self.expr) | ||||
|         expr.eval(frame) | ||||
|         self.result = expr.result | ||||
|         self.explanation = expr.explanation | ||||
| 
 | ||||
| class Stmt(Interpretable): | ||||
|     __view__ = ast.Stmt | ||||
| 
 | ||||
|     def run(self, frame): | ||||
|         for stmt in self.nodes: | ||||
|             stmt = Interpretable(stmt) | ||||
|             stmt.run(frame) | ||||
| 
 | ||||
| 
 | ||||
| def report_failure(e): | ||||
|     explanation = e.node.nice_explanation() | ||||
|     if explanation: | ||||
|         explanation = ", in: " + explanation | ||||
|     else: | ||||
|         explanation = "" | ||||
|     sys.stdout.write("%s: %s%s\n" % (e.exc.__name__, e.value, explanation)) | ||||
| 
 | ||||
| def check(s, frame=None): | ||||
|     from _pytest._code import Frame | ||||
|     if frame is None: | ||||
|         frame = sys._getframe(1) | ||||
|         frame = Frame(frame) | ||||
|     expr = parse(s, 'eval') | ||||
|     assert isinstance(expr, ast.Expression) | ||||
|     node = Interpretable(expr.node) | ||||
|     try: | ||||
|         node.eval(frame) | ||||
|     except passthroughex: | ||||
|         raise | ||||
|     except Failure: | ||||
|         e = sys.exc_info()[1] | ||||
|         report_failure(e) | ||||
|     else: | ||||
|         if not frame.is_true(node.result): | ||||
|             sys.stderr.write("assertion failed: %s\n" % node.nice_explanation()) | ||||
| 
 | ||||
| 
 | ||||
| ########################################################### | ||||
| # API / Entry points | ||||
| # ######################################################### | ||||
| 
 | ||||
| def interpret(source, frame, should_fail=False): | ||||
|     module = Interpretable(parse(source, 'exec').node) | ||||
|     import _pytest._code | ||||
|     if isinstance(frame, py.std.types.FrameType): | ||||
|         frame = _pytest._code.Frame(frame) | ||||
|     try: | ||||
|         module.run(frame) | ||||
|     except Failure: | ||||
|         e = sys.exc_info()[1] | ||||
|         return getfailure(e) | ||||
|     except passthroughex: | ||||
|         raise | ||||
|     except: | ||||
|         import traceback | ||||
|         traceback.print_exc() | ||||
|     if should_fail: | ||||
|         return ("(assertion failed, but when it was re-run for " | ||||
|                 "printing intermediate values, it did not fail.  Suggestions: " | ||||
|                 "compute assert expression before the assert or use --nomagic)") | ||||
|     else: | ||||
|         return None | ||||
| 
 | ||||
| def getmsg(excinfo): | ||||
|     import _pytest._code | ||||
|     if isinstance(excinfo, tuple): | ||||
|         excinfo = _pytest._code.ExceptionInfo(excinfo) | ||||
|     #frame, line = gettbline(tb) | ||||
|     #frame = pytest.code.Frame(frame) | ||||
|     #return interpret(line, frame) | ||||
| 
 | ||||
|     tb = excinfo.traceback[-1] | ||||
|     source = str(tb.statement).strip() | ||||
|     x = interpret(source, tb.frame, should_fail=True) | ||||
|     if not isinstance(x, str): | ||||
|         raise TypeError("interpret returned non-string %r" % (x,)) | ||||
|     return x | ||||
| 
 | ||||
| def getfailure(e): | ||||
|     explanation = e.node.nice_explanation() | ||||
|     if str(e.value): | ||||
|         lines = explanation.split('\n') | ||||
|         lines[0] += "  << %s" % (e.value,) | ||||
|         explanation = '\n'.join(lines) | ||||
|     text = "%s: %s" % (e.exc.__name__, explanation) | ||||
|     if text.startswith('AssertionError: assert '): | ||||
|         text = text[16:] | ||||
|     return text | ||||
| 
 | ||||
| def run(s, frame=None): | ||||
|     import _pytest._code | ||||
|     if frame is None: | ||||
|         frame = sys._getframe(1) | ||||
|         frame = _pytest._code.Frame(frame) | ||||
|     module = Interpretable(parse(s, 'exec').node) | ||||
|     try: | ||||
|         module.run(frame) | ||||
|     except Failure: | ||||
|         e = sys.exc_info()[1] | ||||
|         report_failure(e) | ||||
| 
 | ||||
| 
 | ||||
| if __name__ == '__main__': | ||||
|     # example: | ||||
|     def f(): | ||||
|         return 5 | ||||
|     def g(): | ||||
|         return 3 | ||||
|     def h(x): | ||||
|         return 'never' | ||||
|     check("f() * g() == 5") | ||||
|     check("not f()") | ||||
|     check("not (f() and g() or 0)") | ||||
|     check("f() == g()") | ||||
|     i = 4 | ||||
|     check("i == f()") | ||||
|     check("len(f()) == 0") | ||||
|     check("isinstance(2+3+4, float)") | ||||
| 
 | ||||
|     run("x = i") | ||||
|     check("x == 5") | ||||
| 
 | ||||
|     run("assert not f(), 'oops'") | ||||
|     run("a, b, c = 1, 2") | ||||
|     run("a, b, c = f()") | ||||
| 
 | ||||
|     check("max([f(),g()]) == 4") | ||||
|     check("'hello'[g()] == 'h'") | ||||
|     run("'guk%d' % h(f())") | ||||
|  | @ -0,0 +1,79 @@ | |||
| # copied from python-2.7.3's traceback.py | ||||
| # CHANGES: | ||||
| # - some_str is replaced, trying to create unicode strings | ||||
| # | ||||
| import types | ||||
| 
 | ||||
| def format_exception_only(etype, value): | ||||
|     """Format the exception part of a traceback. | ||||
| 
 | ||||
|     The arguments are the exception type and value such as given by | ||||
|     sys.last_type and sys.last_value. The return value is a list of | ||||
|     strings, each ending in a newline. | ||||
| 
 | ||||
|     Normally, the list contains a single string; however, for | ||||
|     SyntaxError exceptions, it contains several lines that (when | ||||
|     printed) display detailed information about where the syntax | ||||
|     error occurred. | ||||
| 
 | ||||
|     The message indicating which exception occurred is always the last | ||||
|     string in the list. | ||||
| 
 | ||||
|     """ | ||||
| 
 | ||||
|     # An instance should not have a meaningful value parameter, but | ||||
|     # sometimes does, particularly for string exceptions, such as | ||||
|     # >>> raise string1, string2  # deprecated | ||||
|     # | ||||
|     # Clear these out first because issubtype(string1, SyntaxError) | ||||
|     # would throw another exception and mask the original problem. | ||||
|     if (isinstance(etype, BaseException) or | ||||
|         isinstance(etype, types.InstanceType) or | ||||
|         etype is None or type(etype) is str): | ||||
|         return [_format_final_exc_line(etype, value)] | ||||
| 
 | ||||
|     stype = etype.__name__ | ||||
| 
 | ||||
|     if not issubclass(etype, SyntaxError): | ||||
|         return [_format_final_exc_line(stype, value)] | ||||
| 
 | ||||
|     # It was a syntax error; show exactly where the problem was found. | ||||
|     lines = [] | ||||
|     try: | ||||
|         msg, (filename, lineno, offset, badline) = value.args | ||||
|     except Exception: | ||||
|         pass | ||||
|     else: | ||||
|         filename = filename or "<string>" | ||||
|         lines.append('  File "%s", line %d\n' % (filename, lineno)) | ||||
|         if badline is not None: | ||||
|             lines.append('    %s\n' % badline.strip()) | ||||
|             if offset is not None: | ||||
|                 caretspace = badline.rstrip('\n')[:offset].lstrip() | ||||
|                 # non-space whitespace (likes tabs) must be kept for alignment | ||||
|                 caretspace = ((c.isspace() and c or ' ') for c in caretspace) | ||||
|                 # only three spaces to account for offset1 == pos 0 | ||||
|                 lines.append('   %s^\n' % ''.join(caretspace)) | ||||
|         value = msg | ||||
| 
 | ||||
|     lines.append(_format_final_exc_line(stype, value)) | ||||
|     return lines | ||||
| 
 | ||||
| def _format_final_exc_line(etype, value): | ||||
|     """Return a list of a single line -- normal case for format_exception_only""" | ||||
|     valuestr = _some_str(value) | ||||
|     if value is None or not valuestr: | ||||
|         line = "%s\n" % etype | ||||
|     else: | ||||
|         line = "%s: %s\n" % (etype, valuestr) | ||||
|     return line | ||||
| 
 | ||||
| def _some_str(value): | ||||
|     try: | ||||
|         return unicode(value) | ||||
|     except Exception: | ||||
|         try: | ||||
|             return str(value) | ||||
|         except Exception: | ||||
|             pass | ||||
|     return '<unprintable %s object>' % type(value).__name__ | ||||
|  | @ -0,0 +1,96 @@ | |||
| import sys | ||||
| 
 | ||||
| import py | ||||
| from .code import Frame | ||||
| 
 | ||||
| BuiltinAssertionError = py.builtin.builtins.AssertionError | ||||
| 
 | ||||
| _reprcompare = None # if set, will be called by assert reinterp for comparison ops | ||||
| 
 | ||||
| def _format_explanation(explanation): | ||||
|     """This formats an explanation | ||||
| 
 | ||||
|     Normally all embedded newlines are escaped, however there are | ||||
|     three exceptions: \n{, \n} and \n~.  The first two are intended | ||||
|     cover nested explanations, see function and attribute explanations | ||||
|     for examples (.visit_Call(), visit_Attribute()).  The last one is | ||||
|     for when one explanation needs to span multiple lines, e.g. when | ||||
|     displaying diffs. | ||||
|     """ | ||||
|     raw_lines = (explanation or '').split('\n') | ||||
|     # escape newlines not followed by {, } and ~ | ||||
|     lines = [raw_lines[0]] | ||||
|     for l in raw_lines[1:]: | ||||
|         if l.startswith('{') or l.startswith('}') or l.startswith('~'): | ||||
|             lines.append(l) | ||||
|         else: | ||||
|             lines[-1] += '\\n' + l | ||||
| 
 | ||||
|     result = lines[:1] | ||||
|     stack = [0] | ||||
|     stackcnt = [0] | ||||
|     for line in lines[1:]: | ||||
|         if line.startswith('{'): | ||||
|             if stackcnt[-1]: | ||||
|                 s = 'and   ' | ||||
|             else: | ||||
|                 s = 'where ' | ||||
|             stack.append(len(result)) | ||||
|             stackcnt[-1] += 1 | ||||
|             stackcnt.append(0) | ||||
|             result.append(' +' + '  '*(len(stack)-1) + s + line[1:]) | ||||
|         elif line.startswith('}'): | ||||
|             assert line.startswith('}') | ||||
|             stack.pop() | ||||
|             stackcnt.pop() | ||||
|             result[stack[-1]] += line[1:] | ||||
|         else: | ||||
|             assert line.startswith('~') | ||||
|             result.append('  '*len(stack) + line[1:]) | ||||
|     assert len(stack) == 1 | ||||
|     return '\n'.join(result) | ||||
| 
 | ||||
| 
 | ||||
| class AssertionError(BuiltinAssertionError): | ||||
|     def __init__(self, *args): | ||||
|         BuiltinAssertionError.__init__(self, *args) | ||||
|         if args: | ||||
|             try: | ||||
|                 self.msg = str(args[0]) | ||||
|             except py.builtin._sysex: | ||||
|                 raise | ||||
|             except: | ||||
|                 self.msg = "<[broken __repr__] %s at %0xd>" %( | ||||
|                     args[0].__class__, id(args[0])) | ||||
|         else: | ||||
|             f = Frame(sys._getframe(1)) | ||||
|             try: | ||||
|                 source = f.code.fullsource | ||||
|                 if source is not None: | ||||
|                     try: | ||||
|                         source = source.getstatement(f.lineno, assertion=True) | ||||
|                     except IndexError: | ||||
|                         source = None | ||||
|                     else: | ||||
|                         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 = reinterpret(source, f, should_fail=True) | ||||
|             else: | ||||
|                 self.msg = "<could not determine information>" | ||||
|             if not self.args: | ||||
|                 self.args = (self.msg,) | ||||
| 
 | ||||
| if sys.version_info > (3, 0): | ||||
|     AssertionError.__module__ = "builtins" | ||||
|     reinterpret_old = "old reinterpretation not available for py3" | ||||
| else: | ||||
|     from _pytest._code._assertionold import interpret as reinterpret_old | ||||
| if sys.version_info >= (2, 6) or (sys.platform.startswith("java")): | ||||
|     from _pytest._code._assertionnew import interpret as reinterpret | ||||
| else: | ||||
|     reinterpret = reinterpret_old | ||||
| 
 | ||||
|  | @ -0,0 +1,795 @@ | |||
| import sys | ||||
| from inspect import CO_VARARGS, CO_VARKEYWORDS | ||||
| 
 | ||||
| import py | ||||
| 
 | ||||
| builtin_repr = repr | ||||
| 
 | ||||
| reprlib = py.builtin._tryimport('repr', 'reprlib') | ||||
| 
 | ||||
| if sys.version_info[0] >= 3: | ||||
|     from traceback import format_exception_only | ||||
| else: | ||||
|     from ._py2traceback import format_exception_only | ||||
| 
 | ||||
| class Code(object): | ||||
|     """ wrapper around Python code objects """ | ||||
|     def __init__(self, rawcode): | ||||
|         if not hasattr(rawcode, "co_filename"): | ||||
|             rawcode = getrawcode(rawcode) | ||||
|         try: | ||||
|             self.filename = rawcode.co_filename | ||||
|             self.firstlineno = rawcode.co_firstlineno - 1 | ||||
|             self.name = rawcode.co_name | ||||
|         except AttributeError: | ||||
|             raise TypeError("not a code object: %r" %(rawcode,)) | ||||
|         self.raw = rawcode | ||||
| 
 | ||||
|     def __eq__(self, other): | ||||
|         return self.raw == other.raw | ||||
| 
 | ||||
|     def __ne__(self, other): | ||||
|         return not self == other | ||||
| 
 | ||||
|     @property | ||||
|     def path(self): | ||||
|         """ return a path object pointing to source code (note that it | ||||
|         might not point to an actually existing file). """ | ||||
|         p = py.path.local(self.raw.co_filename) | ||||
|         # maybe don't try this checking | ||||
|         if not p.check(): | ||||
|             # XXX maybe try harder like the weird logic | ||||
|             # in the standard lib [linecache.updatecache] does? | ||||
|             p = self.raw.co_filename | ||||
|         return p | ||||
| 
 | ||||
|     @property | ||||
|     def fullsource(self): | ||||
|         """ return a _pytest._code.Source object for the full source file of the code | ||||
|         """ | ||||
|         from _pytest._code import source | ||||
|         full, _ = source.findsource(self.raw) | ||||
|         return full | ||||
| 
 | ||||
|     def source(self): | ||||
|         """ return a _pytest._code.Source object for the code object's source only | ||||
|         """ | ||||
|         # return source only for that part of code | ||||
|         import _pytest._code | ||||
|         return _pytest._code.Source(self.raw) | ||||
| 
 | ||||
|     def getargs(self, var=False): | ||||
|         """ return a tuple with the argument names for the code object | ||||
| 
 | ||||
|             if 'var' is set True also return the names of the variable and | ||||
|             keyword arguments when present | ||||
|         """ | ||||
|         # handfull shortcut for getting args | ||||
|         raw = self.raw | ||||
|         argcount = raw.co_argcount | ||||
|         if var: | ||||
|             argcount += raw.co_flags & CO_VARARGS | ||||
|             argcount += raw.co_flags & CO_VARKEYWORDS | ||||
|         return raw.co_varnames[:argcount] | ||||
| 
 | ||||
| class Frame(object): | ||||
|     """Wrapper around a Python frame holding f_locals and f_globals | ||||
|     in which expressions can be evaluated.""" | ||||
| 
 | ||||
|     def __init__(self, frame): | ||||
|         self.lineno = frame.f_lineno - 1 | ||||
|         self.f_globals = frame.f_globals | ||||
|         self.f_locals = frame.f_locals | ||||
|         self.raw = frame | ||||
|         self.code = Code(frame.f_code) | ||||
| 
 | ||||
|     @property | ||||
|     def statement(self): | ||||
|         """ statement this frame is at """ | ||||
|         import _pytest._code | ||||
|         if self.code.fullsource is None: | ||||
|             return _pytest._code.Source("") | ||||
|         return self.code.fullsource.getstatement(self.lineno) | ||||
| 
 | ||||
|     def eval(self, code, **vars): | ||||
|         """ evaluate 'code' in the frame | ||||
| 
 | ||||
|             'vars' are optional additional local variables | ||||
| 
 | ||||
|             returns the result of the evaluation | ||||
|         """ | ||||
|         f_locals = self.f_locals.copy() | ||||
|         f_locals.update(vars) | ||||
|         return eval(code, self.f_globals, f_locals) | ||||
| 
 | ||||
|     def exec_(self, code, **vars): | ||||
|         """ exec 'code' in the frame | ||||
| 
 | ||||
|             'vars' are optiona; additional local variables | ||||
|         """ | ||||
|         f_locals = self.f_locals.copy() | ||||
|         f_locals.update(vars) | ||||
|         py.builtin.exec_(code, self.f_globals, f_locals ) | ||||
| 
 | ||||
|     def repr(self, object): | ||||
|         """ return a 'safe' (non-recursive, one-line) string repr for 'object' | ||||
|         """ | ||||
|         return py.io.saferepr(object) | ||||
| 
 | ||||
|     def is_true(self, object): | ||||
|         return object | ||||
| 
 | ||||
|     def getargs(self, var=False): | ||||
|         """ return a list of tuples (name, value) for all arguments | ||||
| 
 | ||||
|             if 'var' is set True also include the variable and keyword | ||||
|             arguments when present | ||||
|         """ | ||||
|         retval = [] | ||||
|         for arg in self.code.getargs(var): | ||||
|             try: | ||||
|                 retval.append((arg, self.f_locals[arg])) | ||||
|             except KeyError: | ||||
|                 pass     # this can occur when using Psyco | ||||
|         return retval | ||||
| 
 | ||||
| class TracebackEntry(object): | ||||
|     """ a single entry in a traceback """ | ||||
| 
 | ||||
|     _repr_style = None | ||||
|     exprinfo = None | ||||
| 
 | ||||
|     def __init__(self, rawentry): | ||||
|         self._rawentry = rawentry | ||||
|         self.lineno = rawentry.tb_lineno - 1 | ||||
| 
 | ||||
|     def set_repr_style(self, mode): | ||||
|         assert mode in ("short", "long") | ||||
|         self._repr_style = mode | ||||
| 
 | ||||
|     @property | ||||
|     def frame(self): | ||||
|         import _pytest._code | ||||
|         return _pytest._code.Frame(self._rawentry.tb_frame) | ||||
| 
 | ||||
|     @property | ||||
|     def relline(self): | ||||
|         return self.lineno - self.frame.code.firstlineno | ||||
| 
 | ||||
|     def __repr__(self): | ||||
|         return "<TracebackEntry %s:%d>" %(self.frame.code.path, self.lineno+1) | ||||
| 
 | ||||
|     @property | ||||
|     def statement(self): | ||||
|         """ _pytest._code.Source object for the current statement """ | ||||
|         source = self.frame.code.fullsource | ||||
|         return source.getstatement(self.lineno) | ||||
| 
 | ||||
|     @property | ||||
|     def path(self): | ||||
|         """ path to the source code """ | ||||
|         return self.frame.code.path | ||||
| 
 | ||||
|     def getlocals(self): | ||||
|         return self.frame.f_locals | ||||
|     locals = property(getlocals, None, None, "locals of underlaying frame") | ||||
| 
 | ||||
|     def reinterpret(self): | ||||
|         """Reinterpret the failing statement and returns a detailed information | ||||
|            about what operations are performed.""" | ||||
|         import _pytest._code | ||||
|         if self.exprinfo is None: | ||||
|             source = str(self.statement).strip() | ||||
|             x = _pytest._code._reinterpret(source, self.frame, should_fail=True) | ||||
|             if not isinstance(x, str): | ||||
|                 raise TypeError("interpret returned non-string %r" % (x,)) | ||||
|             self.exprinfo = x | ||||
|         return self.exprinfo | ||||
| 
 | ||||
|     def getfirstlinesource(self): | ||||
|         # on Jython this firstlineno can be -1 apparently | ||||
|         return max(self.frame.code.firstlineno, 0) | ||||
| 
 | ||||
|     def getsource(self, astcache=None): | ||||
|         """ return failing source code. """ | ||||
|         # we use the passed in astcache to not reparse asttrees | ||||
|         # within exception info printing | ||||
|         from _pytest._code.source import getstatementrange_ast | ||||
|         source = self.frame.code.fullsource | ||||
|         if source is None: | ||||
|             return None | ||||
|         key = astnode = None | ||||
|         if astcache is not None: | ||||
|             key = self.frame.code.path | ||||
|             if key is not None: | ||||
|                 astnode = astcache.get(key, None) | ||||
|         start = self.getfirstlinesource() | ||||
|         try: | ||||
|             astnode, _, end = getstatementrange_ast(self.lineno, source, | ||||
|                                                     astnode=astnode) | ||||
|         except SyntaxError: | ||||
|             end = self.lineno + 1 | ||||
|         else: | ||||
|             if key is not None: | ||||
|                 astcache[key] = astnode | ||||
|         return source[start:end] | ||||
| 
 | ||||
|     source = property(getsource) | ||||
| 
 | ||||
|     def ishidden(self): | ||||
|         """ return True if the current frame has a var __tracebackhide__ | ||||
|             resolving to True | ||||
| 
 | ||||
|             mostly for internal use | ||||
|         """ | ||||
|         try: | ||||
|             return self.frame.f_locals['__tracebackhide__'] | ||||
|         except KeyError: | ||||
|             try: | ||||
|                 return self.frame.f_globals['__tracebackhide__'] | ||||
|             except KeyError: | ||||
|                 return False | ||||
| 
 | ||||
|     def __str__(self): | ||||
|         try: | ||||
|             fn = str(self.path) | ||||
|         except py.error.Error: | ||||
|             fn = '???' | ||||
|         name = self.frame.code.name | ||||
|         try: | ||||
|             line = str(self.statement).lstrip() | ||||
|         except KeyboardInterrupt: | ||||
|             raise | ||||
|         except: | ||||
|             line = "???" | ||||
|         return "  File %r:%d in %s\n  %s\n" %(fn, self.lineno+1, name, line) | ||||
| 
 | ||||
|     def name(self): | ||||
|         return self.frame.code.raw.co_name | ||||
|     name = property(name, None, None, "co_name of underlaying code") | ||||
| 
 | ||||
| class Traceback(list): | ||||
|     """ Traceback objects encapsulate and offer higher level | ||||
|         access to Traceback entries. | ||||
|     """ | ||||
|     Entry = TracebackEntry | ||||
|     def __init__(self, tb): | ||||
|         """ initialize from given python traceback object. """ | ||||
|         if hasattr(tb, 'tb_next'): | ||||
|             def f(cur): | ||||
|                 while cur is not None: | ||||
|                     yield self.Entry(cur) | ||||
|                     cur = cur.tb_next | ||||
|             list.__init__(self, f(tb)) | ||||
|         else: | ||||
|             list.__init__(self, tb) | ||||
| 
 | ||||
|     def cut(self, path=None, lineno=None, firstlineno=None, excludepath=None): | ||||
|         """ return a Traceback instance wrapping part of this Traceback | ||||
| 
 | ||||
|             by provding any combination of path, lineno and firstlineno, the | ||||
|             first frame to start the to-be-returned traceback is determined | ||||
| 
 | ||||
|             this allows cutting the first part of a Traceback instance e.g. | ||||
|             for formatting reasons (removing some uninteresting bits that deal | ||||
|             with handling of the exception/traceback) | ||||
|         """ | ||||
|         for x in self: | ||||
|             code = x.frame.code | ||||
|             codepath = code.path | ||||
|             if ((path is None or codepath == path) and | ||||
|                 (excludepath is None or not hasattr(codepath, 'relto') or | ||||
|                  not codepath.relto(excludepath)) and | ||||
|                 (lineno is None or x.lineno == lineno) and | ||||
|                 (firstlineno is None or x.frame.code.firstlineno == firstlineno)): | ||||
|                 return Traceback(x._rawentry) | ||||
|         return self | ||||
| 
 | ||||
|     def __getitem__(self, key): | ||||
|         val = super(Traceback, self).__getitem__(key) | ||||
|         if isinstance(key, type(slice(0))): | ||||
|             val = self.__class__(val) | ||||
|         return val | ||||
| 
 | ||||
|     def filter(self, fn=lambda x: not x.ishidden()): | ||||
|         """ return a Traceback instance with certain items removed | ||||
| 
 | ||||
|             fn is a function that gets a single argument, a TracebackItem | ||||
|             instance, and should return True when the item should be added | ||||
|             to the Traceback, False when not | ||||
| 
 | ||||
|             by default this removes all the TracebackItems which are hidden | ||||
|             (see ishidden() above) | ||||
|         """ | ||||
|         return Traceback(filter(fn, self)) | ||||
| 
 | ||||
|     def getcrashentry(self): | ||||
|         """ return last non-hidden traceback entry that lead | ||||
|         to the exception of a traceback. | ||||
|         """ | ||||
|         for i in range(-1, -len(self)-1, -1): | ||||
|             entry = self[i] | ||||
|             if not entry.ishidden(): | ||||
|                 return entry | ||||
|         return self[-1] | ||||
| 
 | ||||
|     def recursionindex(self): | ||||
|         """ return the index of the frame/TracebackItem where recursion | ||||
|             originates if appropriate, None if no recursion occurred | ||||
|         """ | ||||
|         cache = {} | ||||
|         for i, entry in enumerate(self): | ||||
|             # id for the code.raw is needed to work around | ||||
|             # the strange metaprogramming in the decorator lib from pypi | ||||
|             # which generates code objects that have hash/value equality | ||||
|             #XXX needs a test | ||||
|             key = entry.frame.code.path, id(entry.frame.code.raw), entry.lineno | ||||
|             #print "checking for recursion at", key | ||||
|             l = cache.setdefault(key, []) | ||||
|             if l: | ||||
|                 f = entry.frame | ||||
|                 loc = f.f_locals | ||||
|                 for otherloc in l: | ||||
|                     if f.is_true(f.eval(co_equal, | ||||
|                         __recursioncache_locals_1=loc, | ||||
|                         __recursioncache_locals_2=otherloc)): | ||||
|                         return i | ||||
|             l.append(entry.frame.f_locals) | ||||
|         return None | ||||
| 
 | ||||
| co_equal = compile('__recursioncache_locals_1 == __recursioncache_locals_2', | ||||
|                    '?', 'eval') | ||||
| 
 | ||||
| class ExceptionInfo(object): | ||||
|     """ wraps sys.exc_info() objects and offers | ||||
|         help for navigating the traceback. | ||||
|     """ | ||||
|     _striptext = '' | ||||
|     def __init__(self, tup=None, exprinfo=None): | ||||
|         import _pytest._code | ||||
|         if tup is None: | ||||
|             tup = sys.exc_info() | ||||
|             if exprinfo is None and isinstance(tup[1], AssertionError): | ||||
|                 exprinfo = getattr(tup[1], 'msg', None) | ||||
|                 if exprinfo is None: | ||||
|                     exprinfo = str(tup[1]) | ||||
|                 if exprinfo and exprinfo.startswith('assert '): | ||||
|                     self._striptext = 'AssertionError: ' | ||||
|         self._excinfo = tup | ||||
|         #: the exception class | ||||
|         self.type = tup[0] | ||||
|         #: the exception instance | ||||
|         self.value = tup[1] | ||||
|         #: the exception raw traceback | ||||
|         self.tb = tup[2] | ||||
|         #: the exception type name | ||||
|         self.typename = self.type.__name__ | ||||
|         #: the exception traceback (_pytest._code.Traceback instance) | ||||
|         self.traceback = _pytest._code.Traceback(self.tb) | ||||
| 
 | ||||
|     def __repr__(self): | ||||
|         return "<ExceptionInfo %s tblen=%d>" % (self.typename, len(self.traceback)) | ||||
| 
 | ||||
|     def exconly(self, tryshort=False): | ||||
|         """ return the exception as a string | ||||
| 
 | ||||
|             when 'tryshort' resolves to True, and the exception is a | ||||
|             _pytest._code._AssertionError, only the actual exception part of | ||||
|             the exception representation is returned (so 'AssertionError: ' is | ||||
|             removed from the beginning) | ||||
|         """ | ||||
|         lines = format_exception_only(self.type, self.value) | ||||
|         text = ''.join(lines) | ||||
|         text = text.rstrip() | ||||
|         if tryshort: | ||||
|             if text.startswith(self._striptext): | ||||
|                 text = text[len(self._striptext):] | ||||
|         return text | ||||
| 
 | ||||
|     def errisinstance(self, exc): | ||||
|         """ return True if the exception is an instance of exc """ | ||||
|         return isinstance(self.value, exc) | ||||
| 
 | ||||
|     def _getreprcrash(self): | ||||
|         exconly = self.exconly(tryshort=True) | ||||
|         entry = self.traceback.getcrashentry() | ||||
|         path, lineno = entry.frame.code.raw.co_filename, entry.lineno | ||||
|         return ReprFileLocation(path, lineno+1, exconly) | ||||
| 
 | ||||
|     def getrepr(self, showlocals=False, style="long", | ||||
|             abspath=False, tbfilter=True, funcargs=False): | ||||
|         """ return str()able representation of this exception info. | ||||
|             showlocals: show locals per traceback entry | ||||
|             style: long|short|no|native traceback style | ||||
|             tbfilter: hide entries (where __tracebackhide__ is true) | ||||
| 
 | ||||
|             in case of style==native, tbfilter and showlocals is ignored. | ||||
|         """ | ||||
|         if style == 'native': | ||||
|             return ReprExceptionInfo(ReprTracebackNative( | ||||
|                 py.std.traceback.format_exception( | ||||
|                     self.type, | ||||
|                     self.value, | ||||
|                     self.traceback[0]._rawentry, | ||||
|                 )), self._getreprcrash()) | ||||
| 
 | ||||
|         fmt = FormattedExcinfo(showlocals=showlocals, style=style, | ||||
|             abspath=abspath, tbfilter=tbfilter, funcargs=funcargs) | ||||
|         return fmt.repr_excinfo(self) | ||||
| 
 | ||||
|     def __str__(self): | ||||
|         entry = self.traceback[-1] | ||||
|         loc = ReprFileLocation(entry.path, entry.lineno + 1, self.exconly()) | ||||
|         return str(loc) | ||||
| 
 | ||||
|     def __unicode__(self): | ||||
|         entry = self.traceback[-1] | ||||
|         loc = ReprFileLocation(entry.path, entry.lineno + 1, self.exconly()) | ||||
|         return unicode(loc) | ||||
| 
 | ||||
| 
 | ||||
| class FormattedExcinfo(object): | ||||
|     """ presenting information about failing Functions and Generators. """ | ||||
|     # for traceback entries | ||||
|     flow_marker = ">" | ||||
|     fail_marker = "E" | ||||
| 
 | ||||
|     def __init__(self, showlocals=False, style="long", abspath=True, tbfilter=True, funcargs=False): | ||||
|         self.showlocals = showlocals | ||||
|         self.style = style | ||||
|         self.tbfilter = tbfilter | ||||
|         self.funcargs = funcargs | ||||
|         self.abspath = abspath | ||||
|         self.astcache = {} | ||||
| 
 | ||||
|     def _getindent(self, source): | ||||
|         # figure out indent for given source | ||||
|         try: | ||||
|             s = str(source.getstatement(len(source)-1)) | ||||
|         except KeyboardInterrupt: | ||||
|             raise | ||||
|         except: | ||||
|             try: | ||||
|                 s = str(source[-1]) | ||||
|             except KeyboardInterrupt: | ||||
|                 raise | ||||
|             except: | ||||
|                 return 0 | ||||
|         return 4 + (len(s) - len(s.lstrip())) | ||||
| 
 | ||||
|     def _getentrysource(self, entry): | ||||
|         source = entry.getsource(self.astcache) | ||||
|         if source is not None: | ||||
|             source = source.deindent() | ||||
|         return source | ||||
| 
 | ||||
|     def _saferepr(self, obj): | ||||
|         return py.io.saferepr(obj) | ||||
| 
 | ||||
|     def repr_args(self, entry): | ||||
|         if self.funcargs: | ||||
|             args = [] | ||||
|             for argname, argvalue in entry.frame.getargs(var=True): | ||||
|                 args.append((argname, self._saferepr(argvalue))) | ||||
|             return ReprFuncArgs(args) | ||||
| 
 | ||||
|     def get_source(self, source, line_index=-1, excinfo=None, short=False): | ||||
|         """ return formatted and marked up source lines. """ | ||||
|         import _pytest._code | ||||
|         lines = [] | ||||
|         if source is None or line_index >= len(source.lines): | ||||
|             source = _pytest._code.Source("???") | ||||
|             line_index = 0 | ||||
|         if line_index < 0: | ||||
|             line_index += len(source) | ||||
|         space_prefix = "    " | ||||
|         if short: | ||||
|             lines.append(space_prefix + source.lines[line_index].strip()) | ||||
|         else: | ||||
|             for line in source.lines[:line_index]: | ||||
|                 lines.append(space_prefix + line) | ||||
|             lines.append(self.flow_marker + "   " + source.lines[line_index]) | ||||
|             for line in source.lines[line_index+1:]: | ||||
|                 lines.append(space_prefix + line) | ||||
|         if excinfo is not None: | ||||
|             indent = 4 if short else self._getindent(source) | ||||
|             lines.extend(self.get_exconly(excinfo, indent=indent, markall=True)) | ||||
|         return lines | ||||
| 
 | ||||
|     def get_exconly(self, excinfo, indent=4, markall=False): | ||||
|         lines = [] | ||||
|         indent = " " * indent | ||||
|         # get the real exception information out | ||||
|         exlines = excinfo.exconly(tryshort=True).split('\n') | ||||
|         failindent = self.fail_marker + indent[1:] | ||||
|         for line in exlines: | ||||
|             lines.append(failindent + line) | ||||
|             if not markall: | ||||
|                 failindent = indent | ||||
|         return lines | ||||
| 
 | ||||
|     def repr_locals(self, locals): | ||||
|         if self.showlocals: | ||||
|             lines = [] | ||||
|             keys = [loc for loc in locals if loc[0] != "@"] | ||||
|             keys.sort() | ||||
|             for name in keys: | ||||
|                 value = locals[name] | ||||
|                 if name == '__builtins__': | ||||
|                     lines.append("__builtins__ = <builtins>") | ||||
|                 else: | ||||
|                     # This formatting could all be handled by the | ||||
|                     # _repr() function, which is only reprlib.Repr in | ||||
|                     # disguise, so is very configurable. | ||||
|                     str_repr = self._saferepr(value) | ||||
|                     #if len(str_repr) < 70 or not isinstance(value, | ||||
|                     #                            (list, tuple, dict)): | ||||
|                     lines.append("%-10s = %s" %(name, str_repr)) | ||||
|                     #else: | ||||
|                     #    self._line("%-10s =\\" % (name,)) | ||||
|                     #    # XXX | ||||
|                     #    py.std.pprint.pprint(value, stream=self.excinfowriter) | ||||
|             return ReprLocals(lines) | ||||
| 
 | ||||
|     def repr_traceback_entry(self, entry, excinfo=None): | ||||
|         import _pytest._code | ||||
|         source = self._getentrysource(entry) | ||||
|         if source is None: | ||||
|             source = _pytest._code.Source("???") | ||||
|             line_index = 0 | ||||
|         else: | ||||
|             # entry.getfirstlinesource() can be -1, should be 0 on jython | ||||
|             line_index = entry.lineno - max(entry.getfirstlinesource(), 0) | ||||
| 
 | ||||
|         lines = [] | ||||
|         style = entry._repr_style | ||||
|         if style is None: | ||||
|             style = self.style | ||||
|         if style in ("short", "long"): | ||||
|             short = style == "short" | ||||
|             reprargs = self.repr_args(entry) if not short else None | ||||
|             s = self.get_source(source, line_index, excinfo, short=short) | ||||
|             lines.extend(s) | ||||
|             if short: | ||||
|                 message = "in %s" %(entry.name) | ||||
|             else: | ||||
|                 message = excinfo and excinfo.typename or "" | ||||
|             path = self._makepath(entry.path) | ||||
|             filelocrepr = ReprFileLocation(path, entry.lineno+1, message) | ||||
|             localsrepr = None | ||||
|             if not short: | ||||
|                 localsrepr =  self.repr_locals(entry.locals) | ||||
|             return ReprEntry(lines, reprargs, localsrepr, filelocrepr, style) | ||||
|         if excinfo: | ||||
|             lines.extend(self.get_exconly(excinfo, indent=4)) | ||||
|         return ReprEntry(lines, None, None, None, style) | ||||
| 
 | ||||
|     def _makepath(self, path): | ||||
|         if not self.abspath: | ||||
|             try: | ||||
|                 np = py.path.local().bestrelpath(path) | ||||
|             except OSError: | ||||
|                 return path | ||||
|             if len(np) < len(str(path)): | ||||
|                 path = np | ||||
|         return path | ||||
| 
 | ||||
|     def repr_traceback(self, excinfo): | ||||
|         traceback = excinfo.traceback | ||||
|         if self.tbfilter: | ||||
|             traceback = traceback.filter() | ||||
|         recursionindex = None | ||||
|         if excinfo.errisinstance(RuntimeError): | ||||
|             if "maximum recursion depth exceeded" in str(excinfo.value): | ||||
|                 recursionindex = traceback.recursionindex() | ||||
|         last = traceback[-1] | ||||
|         entries = [] | ||||
|         extraline = None | ||||
|         for index, entry in enumerate(traceback): | ||||
|             einfo = (last == entry) and excinfo or None | ||||
|             reprentry = self.repr_traceback_entry(entry, einfo) | ||||
|             entries.append(reprentry) | ||||
|             if index == recursionindex: | ||||
|                 extraline = "!!! Recursion detected (same locals & position)" | ||||
|                 break | ||||
|         return ReprTraceback(entries, extraline, style=self.style) | ||||
| 
 | ||||
|     def repr_excinfo(self, excinfo): | ||||
|         reprtraceback = self.repr_traceback(excinfo) | ||||
|         reprcrash = excinfo._getreprcrash() | ||||
|         return ReprExceptionInfo(reprtraceback, reprcrash) | ||||
| 
 | ||||
| class TerminalRepr: | ||||
|     def __str__(self): | ||||
|         s = self.__unicode__() | ||||
|         if sys.version_info[0] < 3: | ||||
|             s = s.encode('utf-8') | ||||
|         return s | ||||
| 
 | ||||
|     def __unicode__(self): | ||||
|         # FYI this is called from pytest-xdist's serialization of exception | ||||
|         # information. | ||||
|         io = py.io.TextIO() | ||||
|         tw = py.io.TerminalWriter(file=io) | ||||
|         self.toterminal(tw) | ||||
|         return io.getvalue().strip() | ||||
| 
 | ||||
|     def __repr__(self): | ||||
|         return "<%s instance at %0x>" %(self.__class__, id(self)) | ||||
| 
 | ||||
| 
 | ||||
| class ReprExceptionInfo(TerminalRepr): | ||||
|     def __init__(self, reprtraceback, reprcrash): | ||||
|         self.reprtraceback = reprtraceback | ||||
|         self.reprcrash = reprcrash | ||||
|         self.sections = [] | ||||
| 
 | ||||
|     def addsection(self, name, content, sep="-"): | ||||
|         self.sections.append((name, content, sep)) | ||||
| 
 | ||||
|     def toterminal(self, tw): | ||||
|         self.reprtraceback.toterminal(tw) | ||||
|         for name, content, sep in self.sections: | ||||
|             tw.sep(sep, name) | ||||
|             tw.line(content) | ||||
| 
 | ||||
| class ReprTraceback(TerminalRepr): | ||||
|     entrysep = "_ " | ||||
| 
 | ||||
|     def __init__(self, reprentries, extraline, style): | ||||
|         self.reprentries = reprentries | ||||
|         self.extraline = extraline | ||||
|         self.style = style | ||||
| 
 | ||||
|     def toterminal(self, tw): | ||||
|         # the entries might have different styles | ||||
|         for i, entry in enumerate(self.reprentries): | ||||
|             if entry.style == "long": | ||||
|                 tw.line("") | ||||
|             entry.toterminal(tw) | ||||
|             if i < len(self.reprentries) - 1: | ||||
|                 next_entry = self.reprentries[i+1] | ||||
|                 if entry.style == "long" or \ | ||||
|                    entry.style == "short" and next_entry.style == "long": | ||||
|                     tw.sep(self.entrysep) | ||||
| 
 | ||||
|         if self.extraline: | ||||
|             tw.line(self.extraline) | ||||
| 
 | ||||
| class ReprTracebackNative(ReprTraceback): | ||||
|     def __init__(self, tblines): | ||||
|         self.style = "native" | ||||
|         self.reprentries = [ReprEntryNative(tblines)] | ||||
|         self.extraline = None | ||||
| 
 | ||||
| class ReprEntryNative(TerminalRepr): | ||||
|     style = "native" | ||||
| 
 | ||||
|     def __init__(self, tblines): | ||||
|         self.lines = tblines | ||||
| 
 | ||||
|     def toterminal(self, tw): | ||||
|         tw.write("".join(self.lines)) | ||||
| 
 | ||||
| class ReprEntry(TerminalRepr): | ||||
|     localssep = "_ " | ||||
| 
 | ||||
|     def __init__(self, lines, reprfuncargs, reprlocals, filelocrepr, style): | ||||
|         self.lines = lines | ||||
|         self.reprfuncargs = reprfuncargs | ||||
|         self.reprlocals = reprlocals | ||||
|         self.reprfileloc = filelocrepr | ||||
|         self.style = style | ||||
| 
 | ||||
|     def toterminal(self, tw): | ||||
|         if self.style == "short": | ||||
|             self.reprfileloc.toterminal(tw) | ||||
|             for line in self.lines: | ||||
|                 red = line.startswith("E   ") | ||||
|                 tw.line(line, bold=True, red=red) | ||||
|             #tw.line("") | ||||
|             return | ||||
|         if self.reprfuncargs: | ||||
|             self.reprfuncargs.toterminal(tw) | ||||
|         for line in self.lines: | ||||
|             red = line.startswith("E   ") | ||||
|             tw.line(line, bold=True, red=red) | ||||
|         if self.reprlocals: | ||||
|             #tw.sep(self.localssep, "Locals") | ||||
|             tw.line("") | ||||
|             self.reprlocals.toterminal(tw) | ||||
|         if self.reprfileloc: | ||||
|             if self.lines: | ||||
|                 tw.line("") | ||||
|             self.reprfileloc.toterminal(tw) | ||||
| 
 | ||||
|     def __str__(self): | ||||
|         return "%s\n%s\n%s" % ("\n".join(self.lines), | ||||
|                                self.reprlocals, | ||||
|                                self.reprfileloc) | ||||
| 
 | ||||
| class ReprFileLocation(TerminalRepr): | ||||
|     def __init__(self, path, lineno, message): | ||||
|         self.path = str(path) | ||||
|         self.lineno = lineno | ||||
|         self.message = message | ||||
| 
 | ||||
|     def toterminal(self, tw): | ||||
|         # filename and lineno output for each entry, | ||||
|         # using an output format that most editors unterstand | ||||
|         msg = self.message | ||||
|         i = msg.find("\n") | ||||
|         if i != -1: | ||||
|             msg = msg[:i] | ||||
|         tw.line("%s:%s: %s" %(self.path, self.lineno, msg)) | ||||
| 
 | ||||
| class ReprLocals(TerminalRepr): | ||||
|     def __init__(self, lines): | ||||
|         self.lines = lines | ||||
| 
 | ||||
|     def toterminal(self, tw): | ||||
|         for line in self.lines: | ||||
|             tw.line(line) | ||||
| 
 | ||||
| class ReprFuncArgs(TerminalRepr): | ||||
|     def __init__(self, args): | ||||
|         self.args = args | ||||
| 
 | ||||
|     def toterminal(self, tw): | ||||
|         if self.args: | ||||
|             linesofar = "" | ||||
|             for name, value in self.args: | ||||
|                 ns = "%s = %s" %(name, value) | ||||
|                 if len(ns) + len(linesofar) + 2 > tw.fullwidth: | ||||
|                     if linesofar: | ||||
|                         tw.line(linesofar) | ||||
|                     linesofar =  ns | ||||
|                 else: | ||||
|                     if linesofar: | ||||
|                         linesofar += ", " + ns | ||||
|                     else: | ||||
|                         linesofar = ns | ||||
|             if linesofar: | ||||
|                 tw.line(linesofar) | ||||
|             tw.line("") | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| oldbuiltins = {} | ||||
| 
 | ||||
| def patch_builtins(assertion=True, compile=True): | ||||
|     """ put compile and AssertionError builtins to Python's builtins. """ | ||||
|     if assertion: | ||||
|         from _pytest._code import assertion | ||||
|         l = oldbuiltins.setdefault('AssertionError', []) | ||||
|         l.append(py.builtin.builtins.AssertionError) | ||||
|         py.builtin.builtins.AssertionError = assertion.AssertionError | ||||
|     if compile: | ||||
|         import _pytest._code | ||||
|         l = oldbuiltins.setdefault('compile', []) | ||||
|         l.append(py.builtin.builtins.compile) | ||||
|         py.builtin.builtins.compile = _pytest._code.compile | ||||
| 
 | ||||
| def unpatch_builtins(assertion=True, compile=True): | ||||
|     """ remove compile and AssertionError builtins from Python builtins. """ | ||||
|     if assertion: | ||||
|         py.builtin.builtins.AssertionError = oldbuiltins['AssertionError'].pop() | ||||
|     if compile: | ||||
|         py.builtin.builtins.compile = oldbuiltins['compile'].pop() | ||||
| 
 | ||||
| def getrawcode(obj, trycall=True): | ||||
|     """ return code object for given function. """ | ||||
|     try: | ||||
|         return obj.__code__ | ||||
|     except AttributeError: | ||||
|         obj = getattr(obj, 'im_func', obj) | ||||
|         obj = getattr(obj, 'func_code', obj) | ||||
|         obj = getattr(obj, 'f_code', obj) | ||||
|         obj = getattr(obj, '__code__', obj) | ||||
|         if trycall and not hasattr(obj, 'co_firstlineno'): | ||||
|             if hasattr(obj, '__call__') and not py.std.inspect.isclass(obj): | ||||
|                 x = getrawcode(obj.__call__, trycall=False) | ||||
|                 if hasattr(x, 'co_firstlineno'): | ||||
|                     return x | ||||
|         return obj | ||||
| 
 | ||||
|  | @ -0,0 +1,421 @@ | |||
| from __future__ import generators | ||||
| 
 | ||||
| from bisect import bisect_right | ||||
| import sys | ||||
| import inspect, tokenize | ||||
| import py | ||||
| from types import ModuleType | ||||
| cpy_compile = compile | ||||
| 
 | ||||
| try: | ||||
|     import _ast | ||||
|     from _ast import PyCF_ONLY_AST as _AST_FLAG | ||||
| except ImportError: | ||||
|     _AST_FLAG = 0 | ||||
|     _ast = None | ||||
| 
 | ||||
| 
 | ||||
| class Source(object): | ||||
|     """ a immutable object holding a source code fragment, | ||||
|         possibly deindenting it. | ||||
|     """ | ||||
|     _compilecounter = 0 | ||||
|     def __init__(self, *parts, **kwargs): | ||||
|         self.lines = lines = [] | ||||
|         de = kwargs.get('deindent', True) | ||||
|         rstrip = kwargs.get('rstrip', True) | ||||
|         for part in parts: | ||||
|             if not part: | ||||
|                 partlines = [] | ||||
|             if isinstance(part, Source): | ||||
|                 partlines = part.lines | ||||
|             elif isinstance(part, (tuple, list)): | ||||
|                 partlines = [x.rstrip("\n") for x in part] | ||||
|             elif isinstance(part, py.builtin._basestring): | ||||
|                 partlines = part.split('\n') | ||||
|                 if rstrip: | ||||
|                     while partlines: | ||||
|                         if partlines[-1].strip(): | ||||
|                             break | ||||
|                         partlines.pop() | ||||
|             else: | ||||
|                 partlines = getsource(part, deindent=de).lines | ||||
|             if de: | ||||
|                 partlines = deindent(partlines) | ||||
|             lines.extend(partlines) | ||||
| 
 | ||||
|     def __eq__(self, other): | ||||
|         try: | ||||
|             return self.lines == other.lines | ||||
|         except AttributeError: | ||||
|             if isinstance(other, str): | ||||
|                 return str(self) == other | ||||
|             return False | ||||
| 
 | ||||
|     def __getitem__(self, key): | ||||
|         if isinstance(key, int): | ||||
|             return self.lines[key] | ||||
|         else: | ||||
|             if key.step not in (None, 1): | ||||
|                 raise IndexError("cannot slice a Source with a step") | ||||
|             return self.__getslice__(key.start, key.stop) | ||||
| 
 | ||||
|     def __len__(self): | ||||
|         return len(self.lines) | ||||
| 
 | ||||
|     def __getslice__(self, start, end): | ||||
|         newsource = Source() | ||||
|         newsource.lines = self.lines[start:end] | ||||
|         return newsource | ||||
| 
 | ||||
|     def strip(self): | ||||
|         """ return new source object with trailing | ||||
|             and leading blank lines removed. | ||||
|         """ | ||||
|         start, end = 0, len(self) | ||||
|         while start < end and not self.lines[start].strip(): | ||||
|             start += 1 | ||||
|         while end > start and not self.lines[end-1].strip(): | ||||
|             end -= 1 | ||||
|         source = Source() | ||||
|         source.lines[:] = self.lines[start:end] | ||||
|         return source | ||||
| 
 | ||||
|     def putaround(self, before='', after='', indent=' ' * 4): | ||||
|         """ return a copy of the source object with | ||||
|             'before' and 'after' wrapped around it. | ||||
|         """ | ||||
|         before = Source(before) | ||||
|         after = Source(after) | ||||
|         newsource = Source() | ||||
|         lines = [ (indent + line) for line in self.lines] | ||||
|         newsource.lines = before.lines + lines +  after.lines | ||||
|         return newsource | ||||
| 
 | ||||
|     def indent(self, indent=' ' * 4): | ||||
|         """ return a copy of the source object with | ||||
|             all lines indented by the given indent-string. | ||||
|         """ | ||||
|         newsource = Source() | ||||
|         newsource.lines = [(indent+line) for line in self.lines] | ||||
|         return newsource | ||||
| 
 | ||||
|     def getstatement(self, lineno, assertion=False): | ||||
|         """ return Source statement which contains the | ||||
|             given linenumber (counted from 0). | ||||
|         """ | ||||
|         start, end = self.getstatementrange(lineno, assertion) | ||||
|         return self[start:end] | ||||
| 
 | ||||
|     def getstatementrange(self, lineno, assertion=False): | ||||
|         """ return (start, end) tuple which spans the minimal | ||||
|             statement region which containing the given lineno. | ||||
|         """ | ||||
|         if not (0 <= lineno < len(self)): | ||||
|             raise IndexError("lineno out of range") | ||||
|         ast, start, end = getstatementrange_ast(lineno, self) | ||||
|         return start, end | ||||
| 
 | ||||
|     def deindent(self, offset=None): | ||||
|         """ return a new source object deindented by offset. | ||||
|             If offset is None then guess an indentation offset from | ||||
|             the first non-blank line.  Subsequent lines which have a | ||||
|             lower indentation offset will be copied verbatim as | ||||
|             they are assumed to be part of multilines. | ||||
|         """ | ||||
|         # XXX maybe use the tokenizer to properly handle multiline | ||||
|         #     strings etc.pp? | ||||
|         newsource = Source() | ||||
|         newsource.lines[:] = deindent(self.lines, offset) | ||||
|         return newsource | ||||
| 
 | ||||
|     def isparseable(self, deindent=True): | ||||
|         """ return True if source is parseable, heuristically | ||||
|             deindenting it by default. | ||||
|         """ | ||||
|         try: | ||||
|             import parser | ||||
|         except ImportError: | ||||
|             syntax_checker = lambda x: compile(x, 'asd', 'exec') | ||||
|         else: | ||||
|             syntax_checker = parser.suite | ||||
| 
 | ||||
|         if deindent: | ||||
|             source = str(self.deindent()) | ||||
|         else: | ||||
|             source = str(self) | ||||
|         try: | ||||
|             #compile(source+'\n', "x", "exec") | ||||
|             syntax_checker(source+'\n') | ||||
|         except KeyboardInterrupt: | ||||
|             raise | ||||
|         except Exception: | ||||
|             return False | ||||
|         else: | ||||
|             return True | ||||
| 
 | ||||
|     def __str__(self): | ||||
|         return "\n".join(self.lines) | ||||
| 
 | ||||
|     def compile(self, filename=None, mode='exec', | ||||
|                 flag=generators.compiler_flag, | ||||
|                 dont_inherit=0, _genframe=None): | ||||
|         """ return compiled code object. if filename is None | ||||
|             invent an artificial filename which displays | ||||
|             the source/line position of the caller frame. | ||||
|         """ | ||||
|         if not filename or py.path.local(filename).check(file=0): | ||||
|             if _genframe is None: | ||||
|                 _genframe = sys._getframe(1) # the caller | ||||
|             fn,lineno = _genframe.f_code.co_filename, _genframe.f_lineno | ||||
|             base = "<%d-codegen " % self._compilecounter | ||||
|             self.__class__._compilecounter += 1 | ||||
|             if not filename: | ||||
|                 filename = base + '%s:%d>' % (fn, lineno) | ||||
|             else: | ||||
|                 filename = base + '%r %s:%d>' % (filename, fn, lineno) | ||||
|         source = "\n".join(self.lines) + '\n' | ||||
|         try: | ||||
|             co = cpy_compile(source, filename, mode, flag) | ||||
|         except SyntaxError: | ||||
|             ex = sys.exc_info()[1] | ||||
|             # re-represent syntax errors from parsing python strings | ||||
|             msglines = self.lines[:ex.lineno] | ||||
|             if ex.offset: | ||||
|                 msglines.append(" "*ex.offset + '^') | ||||
|             msglines.append("(code was compiled probably from here: %s)" % filename) | ||||
|             newex = SyntaxError('\n'.join(msglines)) | ||||
|             newex.offset = ex.offset | ||||
|             newex.lineno = ex.lineno | ||||
|             newex.text = ex.text | ||||
|             raise newex | ||||
|         else: | ||||
|             if flag & _AST_FLAG: | ||||
|                 return co | ||||
|             lines = [(x + "\n") for x in self.lines] | ||||
|             if sys.version_info[0] >= 3: | ||||
|                 # XXX py3's inspect.getsourcefile() checks for a module | ||||
|                 # and a pep302 __loader__ ... we don't have a module | ||||
|                 # at code compile-time so we need to fake it here | ||||
|                 m = ModuleType("_pycodecompile_pseudo_module") | ||||
|                 py.std.inspect.modulesbyfile[filename] = None | ||||
|                 py.std.sys.modules[None] = m | ||||
|                 m.__loader__ = 1 | ||||
|             py.std.linecache.cache[filename] = (1, None, lines, filename) | ||||
|             return co | ||||
| 
 | ||||
| # | ||||
| # public API shortcut functions | ||||
| # | ||||
| 
 | ||||
| def compile_(source, filename=None, mode='exec', flags= | ||||
|             generators.compiler_flag, dont_inherit=0): | ||||
|     """ compile the given source to a raw code object, | ||||
|         and maintain an internal cache which allows later | ||||
|         retrieval of the source code for the code object | ||||
|         and any recursively created code objects. | ||||
|     """ | ||||
|     if _ast is not None and isinstance(source, _ast.AST): | ||||
|         # XXX should Source support having AST? | ||||
|         return cpy_compile(source, filename, mode, flags, dont_inherit) | ||||
|     _genframe = sys._getframe(1) # the caller | ||||
|     s = Source(source) | ||||
|     co = s.compile(filename, mode, flags, _genframe=_genframe) | ||||
|     return co | ||||
| 
 | ||||
| 
 | ||||
| def getfslineno(obj): | ||||
|     """ Return source location (path, lineno) for the given object. | ||||
|     If the source cannot be determined return ("", -1) | ||||
|     """ | ||||
|     import _pytest._code | ||||
|     try: | ||||
|         code = _pytest._code.Code(obj) | ||||
|     except TypeError: | ||||
|         try: | ||||
|             fn = (py.std.inspect.getsourcefile(obj) or | ||||
|                   py.std.inspect.getfile(obj)) | ||||
|         except TypeError: | ||||
|             return "", -1 | ||||
| 
 | ||||
|         fspath = fn and py.path.local(fn) or None | ||||
|         lineno = -1 | ||||
|         if fspath: | ||||
|             try: | ||||
|                 _, lineno = findsource(obj) | ||||
|             except IOError: | ||||
|                 pass | ||||
|     else: | ||||
|         fspath = code.path | ||||
|         lineno = code.firstlineno | ||||
|     assert isinstance(lineno, int) | ||||
|     return fspath, lineno | ||||
| 
 | ||||
| # | ||||
| # helper functions | ||||
| # | ||||
| 
 | ||||
| def findsource(obj): | ||||
|     try: | ||||
|         sourcelines, lineno = py.std.inspect.findsource(obj) | ||||
|     except py.builtin._sysex: | ||||
|         raise | ||||
|     except: | ||||
|         return None, -1 | ||||
|     source = Source() | ||||
|     source.lines = [line.rstrip() for line in sourcelines] | ||||
|     return source, lineno | ||||
| 
 | ||||
| def getsource(obj, **kwargs): | ||||
|     import _pytest._code | ||||
|     obj = _pytest._code.getrawcode(obj) | ||||
|     try: | ||||
|         strsrc = inspect.getsource(obj) | ||||
|     except IndentationError: | ||||
|         strsrc = "\"Buggy python version consider upgrading, cannot get source\"" | ||||
|     assert isinstance(strsrc, str) | ||||
|     return Source(strsrc, **kwargs) | ||||
| 
 | ||||
| def deindent(lines, offset=None): | ||||
|     if offset is None: | ||||
|         for line in lines: | ||||
|             line = line.expandtabs() | ||||
|             s = line.lstrip() | ||||
|             if s: | ||||
|                 offset = len(line)-len(s) | ||||
|                 break | ||||
|         else: | ||||
|             offset = 0 | ||||
|     if offset == 0: | ||||
|         return list(lines) | ||||
|     newlines = [] | ||||
|     def readline_generator(lines): | ||||
|         for line in lines: | ||||
|             yield line + '\n' | ||||
|         while True: | ||||
|             yield '' | ||||
| 
 | ||||
|     it = readline_generator(lines) | ||||
| 
 | ||||
|     try: | ||||
|         for _, _, (sline, _), (eline, _), _ in tokenize.generate_tokens(lambda: next(it)): | ||||
|             if sline > len(lines): | ||||
|                 break # End of input reached | ||||
|             if sline > len(newlines): | ||||
|                 line = lines[sline - 1].expandtabs() | ||||
|                 if line.lstrip() and line[:offset].isspace(): | ||||
|                     line = line[offset:] # Deindent | ||||
|                 newlines.append(line) | ||||
| 
 | ||||
|             for i in range(sline, eline): | ||||
|                 # Don't deindent continuing lines of | ||||
|                 # multiline tokens (i.e. multiline strings) | ||||
|                 newlines.append(lines[i]) | ||||
|     except (IndentationError, tokenize.TokenError): | ||||
|         pass | ||||
|     # Add any lines we didn't see. E.g. if an exception was raised. | ||||
|     newlines.extend(lines[len(newlines):]) | ||||
|     return newlines | ||||
| 
 | ||||
| 
 | ||||
| def get_statement_startend2(lineno, node): | ||||
|     import ast | ||||
|     # flatten all statements and except handlers into one lineno-list | ||||
|     # AST's line numbers start indexing at 1 | ||||
|     l = [] | ||||
|     for x in ast.walk(node): | ||||
|         if isinstance(x, _ast.stmt) or isinstance(x, _ast.ExceptHandler): | ||||
|             l.append(x.lineno - 1) | ||||
|             for name in "finalbody", "orelse": | ||||
|                 val = getattr(x, name, None) | ||||
|                 if val: | ||||
|                     # treat the finally/orelse part as its own statement | ||||
|                     l.append(val[0].lineno - 1 - 1) | ||||
|     l.sort() | ||||
|     insert_index = bisect_right(l, lineno) | ||||
|     start = l[insert_index - 1] | ||||
|     if insert_index >= len(l): | ||||
|         end = None | ||||
|     else: | ||||
|         end = l[insert_index] | ||||
|     return start, end | ||||
| 
 | ||||
| 
 | ||||
| def getstatementrange_ast(lineno, source, assertion=False, astnode=None): | ||||
|     if astnode is None: | ||||
|         content = str(source) | ||||
|         if sys.version_info < (2,7): | ||||
|             content += "\n" | ||||
|         try: | ||||
|             astnode = compile(content, "source", "exec", 1024)  # 1024 for AST | ||||
|         except ValueError: | ||||
|             start, end = getstatementrange_old(lineno, source, assertion) | ||||
|             return None, start, end | ||||
|     start, end = get_statement_startend2(lineno, astnode) | ||||
|     # we need to correct the end: | ||||
|     # - ast-parsing strips comments | ||||
|     # - there might be empty lines | ||||
|     # - we might have lesser indented code blocks at the end | ||||
|     if end is None: | ||||
|         end = len(source.lines) | ||||
| 
 | ||||
|     if end > start + 1: | ||||
|         # make sure we don't span differently indented code blocks | ||||
|         # by using the BlockFinder helper used which inspect.getsource() uses itself | ||||
|         block_finder = inspect.BlockFinder() | ||||
|         # if we start with an indented line, put blockfinder to "started" mode | ||||
|         block_finder.started = source.lines[start][0].isspace() | ||||
|         it = ((x + "\n") for x in source.lines[start:end]) | ||||
|         try: | ||||
|             for tok in tokenize.generate_tokens(lambda: next(it)): | ||||
|                 block_finder.tokeneater(*tok) | ||||
|         except (inspect.EndOfBlock, IndentationError): | ||||
|             end = block_finder.last + start | ||||
|         except Exception: | ||||
|             pass | ||||
| 
 | ||||
|     # the end might still point to a comment or empty line, correct it | ||||
|     while end: | ||||
|         line = source.lines[end - 1].lstrip() | ||||
|         if line.startswith("#") or not line: | ||||
|             end -= 1 | ||||
|         else: | ||||
|             break | ||||
|     return astnode, start, end | ||||
| 
 | ||||
| 
 | ||||
| def getstatementrange_old(lineno, source, assertion=False): | ||||
|     """ return (start, end) tuple which spans the minimal | ||||
|         statement region which containing the given lineno. | ||||
|         raise an IndexError if no such statementrange can be found. | ||||
|     """ | ||||
|     # XXX this logic is only used on python2.4 and below | ||||
|     # 1. find the start of the statement | ||||
|     from codeop import compile_command | ||||
|     for start in range(lineno, -1, -1): | ||||
|         if assertion: | ||||
|             line = source.lines[start] | ||||
|             # the following lines are not fully tested, change with care | ||||
|             if 'super' in line and 'self' in line and '__init__' in line: | ||||
|                 raise IndexError("likely a subclass") | ||||
|             if "assert" not in line and "raise" not in line: | ||||
|                 continue | ||||
|         trylines = source.lines[start:lineno+1] | ||||
|         # quick hack to prepare parsing an indented line with | ||||
|         # compile_command() (which errors on "return" outside defs) | ||||
|         trylines.insert(0, 'def xxx():') | ||||
|         trysource = '\n '.join(trylines) | ||||
|         #              ^ space here | ||||
|         try: | ||||
|             compile_command(trysource) | ||||
|         except (SyntaxError, OverflowError, ValueError): | ||||
|             continue | ||||
| 
 | ||||
|         # 2. find the end of the statement | ||||
|         for end in range(lineno+1, len(source)+1): | ||||
|             trysource = source[start:end] | ||||
|             if trysource.isparseable(): | ||||
|                 return start, end | ||||
|     raise SyntaxError("no valid source range around line %d " % (lineno,)) | ||||
| 
 | ||||
| 
 | ||||
|  | @ -3,6 +3,8 @@ Find intermediate evalutation results in assert statements through builtin AST. | |||
| """ | ||||
| import ast | ||||
| import sys | ||||
| 
 | ||||
| import _pytest._code | ||||
| import py | ||||
| from _pytest.assertion import util | ||||
| u = py.builtin._totext | ||||
|  | @ -26,7 +28,7 @@ class AssertionError(util.BuiltinAssertionError): | |||
|                     "<[broken __repr__] %s at %0xd>" | ||||
|                     % (toprint.__class__, id(toprint))) | ||||
|         else: | ||||
|             f = py.code.Frame(sys._getframe(1)) | ||||
|             f = _pytest._code.Frame(sys._getframe(1)) | ||||
|             try: | ||||
|                 source = f.code.fullsource | ||||
|                 if source is not None: | ||||
|  | @ -102,7 +104,7 @@ def reinterpret(source, frame, should_fail=False): | |||
| 
 | ||||
| def run(offending_line, frame=None): | ||||
|     if frame is None: | ||||
|         frame = py.code.Frame(sys._getframe(1)) | ||||
|         frame = _pytest._code.Frame(sys._getframe(1)) | ||||
|     return reinterpret(offending_line, frame) | ||||
| 
 | ||||
| def getfailure(e): | ||||
|  |  | |||
|  | @ -1,6 +1,7 @@ | |||
| """Utilities for assertion debugging""" | ||||
| import pprint | ||||
| 
 | ||||
| import _pytest._code | ||||
| import py | ||||
| try: | ||||
|     from collections import Sequence | ||||
|  | @ -179,7 +180,7 @@ def assertrepr_compare(config, op, left, right): | |||
|         explanation = [ | ||||
|             u('(pytest_assertion plugin: representation of details failed.  ' | ||||
|               'Probably an object has a faulty __repr__.)'), | ||||
|             u(py.code.ExceptionInfo())] | ||||
|             u(_pytest._code.ExceptionInfo())] | ||||
| 
 | ||||
|     if not explanation: | ||||
|         return None | ||||
|  |  | |||
|  | @ -8,6 +8,7 @@ import warnings | |||
| import py | ||||
| # DON't import pytest here because it causes import cycle troubles | ||||
| import sys, os | ||||
| import _pytest._code | ||||
| import _pytest.hookspec  # the extension point definitions | ||||
| from _pytest._pluggy import PluginManager, HookimplMarker, HookspecMarker | ||||
| 
 | ||||
|  | @ -158,7 +159,7 @@ class PytestPluginManager(PluginManager): | |||
|         Use :py:meth:`pluggy.PluginManager.add_hookspecs` instead. | ||||
|         """ | ||||
|         warning = dict(code="I2", | ||||
|                        fslocation=py.code.getfslineno(sys._getframe(1)), | ||||
|                        fslocation=_pytest._code.getfslineno(sys._getframe(1)), | ||||
|                        nodeid=None, | ||||
|                        message="use pluginmanager.add_hookspecs instead of " | ||||
|                                "deprecated addhooks() method.") | ||||
|  | @ -195,7 +196,7 @@ class PytestPluginManager(PluginManager): | |||
|     def _verify_hook(self, hook, hookmethod): | ||||
|         super(PytestPluginManager, self)._verify_hook(hook, hookmethod) | ||||
|         if "__multicall__" in hookmethod.argnames: | ||||
|             fslineno = py.code.getfslineno(hookmethod.function) | ||||
|             fslineno = _pytest._code.getfslineno(hookmethod.function) | ||||
|             warning = dict(code="I1", | ||||
|                            fslocation=fslineno, | ||||
|                            nodeid=None, | ||||
|  |  | |||
|  | @ -1,9 +1,13 @@ | |||
| """ discover and run doctests in modules and test files.""" | ||||
| from __future__ import absolute_import | ||||
| 
 | ||||
| import traceback | ||||
| import pytest, py | ||||
| 
 | ||||
| import py | ||||
| import pytest | ||||
| from _pytest._code.code import TerminalRepr, ReprFileLocation, ExceptionInfo | ||||
| from _pytest.python import FixtureRequest | ||||
| from py._code.code import TerminalRepr, ReprFileLocation | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| def pytest_addoption(parser): | ||||
|  | @ -107,7 +111,7 @@ class DoctestItem(pytest.Item): | |||
|                 lines += checker.output_difference(example, | ||||
|                         doctestfailure.got, REPORT_UDIFF).split("\n") | ||||
|             else: | ||||
|                 inner_excinfo = py.code.ExceptionInfo(excinfo.value.exc_info) | ||||
|                 inner_excinfo = ExceptionInfo(excinfo.value.exc_info) | ||||
|                 lines += ["UNEXPECTED EXCEPTION: %s" % | ||||
|                             repr(inner_excinfo.value)] | ||||
|                 lines += traceback.format_exception(*excinfo.value.exc_info) | ||||
|  |  | |||
|  | @ -1,9 +1,13 @@ | |||
| """ core implementation of testing process: init, session, runtest loop. """ | ||||
| import imp | ||||
| import os | ||||
| import re | ||||
| import sys | ||||
| 
 | ||||
| import _pytest | ||||
| import _pytest._code | ||||
| import py | ||||
| import pytest, _pytest | ||||
| import os, sys, imp | ||||
| import pytest | ||||
| try: | ||||
|     from collections import MutableMapping as MappingMixin | ||||
| except ImportError: | ||||
|  | @ -91,11 +95,11 @@ def wrap_session(config, doit): | |||
|         except pytest.UsageError: | ||||
|             raise | ||||
|         except KeyboardInterrupt: | ||||
|             excinfo = py.code.ExceptionInfo() | ||||
|             excinfo = _pytest._code.ExceptionInfo() | ||||
|             config.hook.pytest_keyboard_interrupt(excinfo=excinfo) | ||||
|             session.exitstatus = EXIT_INTERRUPTED | ||||
|         except: | ||||
|             excinfo = py.code.ExceptionInfo() | ||||
|             excinfo = _pytest._code.ExceptionInfo() | ||||
|             config.notify_exception(excinfo, config.option) | ||||
|             session.exitstatus = EXIT_INTERNALERROR | ||||
|             if excinfo.errisinstance(SystemExit): | ||||
|  |  | |||
|  | @ -1,19 +1,20 @@ | |||
| """ (disabled by default) support for testing pytest and pytest plugins. """ | ||||
| import gc | ||||
| import sys | ||||
| import traceback | ||||
| import os | ||||
| import codecs | ||||
| import re | ||||
| import time | ||||
| import gc | ||||
| import os | ||||
| import platform | ||||
| from fnmatch import fnmatch | ||||
| import re | ||||
| import subprocess | ||||
| import sys | ||||
| import time | ||||
| import traceback | ||||
| from fnmatch import fnmatch | ||||
| 
 | ||||
| import py | ||||
| import pytest | ||||
| from py.builtin import print_ | ||||
| 
 | ||||
| from _pytest._code import Source | ||||
| import py | ||||
| import pytest | ||||
| from _pytest.main import Session, EXIT_OK | ||||
| 
 | ||||
| 
 | ||||
|  | @ -472,7 +473,7 @@ class Testdir: | |||
|         ret = None | ||||
|         for name, value in items: | ||||
|             p = self.tmpdir.join(name).new(ext=ext) | ||||
|             source = py.code.Source(value) | ||||
|             source = Source(value) | ||||
|             def my_totext(s, encoding="utf-8"): | ||||
|                 if py.builtin._isbytes(s): | ||||
|                     s = py.builtin._totext(s, encoding=encoding) | ||||
|  | @ -835,7 +836,7 @@ class Testdir: | |||
|            to the temporarly directory to ensure it is a package. | ||||
| 
 | ||||
|         """ | ||||
|         kw = {self.request.function.__name__: py.code.Source(source).strip()} | ||||
|         kw = {self.request.function.__name__: Source(source).strip()} | ||||
|         path = self.makepyfile(**kw) | ||||
|         if withinit: | ||||
|             self.makepyfile(__init__ = "#") | ||||
|  | @ -1041,8 +1042,8 @@ class LineMatcher: | |||
| 
 | ||||
|     def _getlines(self, lines2): | ||||
|         if isinstance(lines2, str): | ||||
|             lines2 = py.code.Source(lines2) | ||||
|         if isinstance(lines2, py.code.Source): | ||||
|             lines2 = Source(lines2) | ||||
|         if isinstance(lines2, Source): | ||||
|             lines2 = lines2.strip().lines | ||||
|         return lines2 | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,14 +1,15 @@ | |||
| """ Python test discovery, setup and run of test functions. """ | ||||
| import re | ||||
| import fnmatch | ||||
| import functools | ||||
| import py | ||||
| import inspect | ||||
| import re | ||||
| import types | ||||
| import sys | ||||
| 
 | ||||
| import py | ||||
| import pytest | ||||
| from _pytest._code.code import TerminalRepr | ||||
| from _pytest.mark import MarkDecorator, MarkerError | ||||
| from py._code.code import TerminalRepr | ||||
| 
 | ||||
| try: | ||||
|     import enum | ||||
|  | @ -86,7 +87,7 @@ def getfslineno(obj): | |||
|     obj = get_real_func(obj) | ||||
|     if hasattr(obj, 'place_as'): | ||||
|         obj = obj.place_as | ||||
|     fslineno = py.code.getfslineno(obj) | ||||
|     fslineno = _pytest._code.getfslineno(obj) | ||||
|     assert isinstance(fslineno[1], int), obj | ||||
|     return fslineno | ||||
| 
 | ||||
|  | @ -331,7 +332,7 @@ def pytest_pycollect_makeitem(collector, name, obj): | |||
| 
 | ||||
| def is_generator(func): | ||||
|     try: | ||||
|         return py.code.getrawcode(func).co_flags & 32 # generator function | ||||
|         return _pytest._code.getrawcode(func).co_flags & 32 # generator function | ||||
|     except AttributeError: # builtin functions have no bytecode | ||||
|         # assume them to not be generators | ||||
|         return False | ||||
|  | @ -610,7 +611,7 @@ class Module(pytest.File, PyCollector): | |||
|             mod = self.fspath.pyimport(ensuresyspath=importmode) | ||||
|         except SyntaxError: | ||||
|             raise self.CollectError( | ||||
|                 py.code.ExceptionInfo().getrepr(style="short")) | ||||
|                 _pytest._code.ExceptionInfo().getrepr(style="short")) | ||||
|         except self.fspath.ImportMismatchError: | ||||
|             e = sys.exc_info()[1] | ||||
|             raise self.CollectError( | ||||
|  | @ -716,7 +717,7 @@ class FunctionMixin(PyobjMixin): | |||
| 
 | ||||
|     def _prunetraceback(self, excinfo): | ||||
|         if hasattr(self, '_obj') and not self.config.option.fulltrace: | ||||
|             code = py.code.Code(get_real_func(self.obj)) | ||||
|             code = _pytest._code.Code(get_real_func(self.obj)) | ||||
|             path, firstlineno = code.path, code.firstlineno | ||||
|             traceback = excinfo.traceback | ||||
|             ntraceback = traceback.cut(path=path, firstlineno=firstlineno) | ||||
|  | @ -1202,10 +1203,10 @@ def getlocation(function, curdir): | |||
| # builtin pytest.raises helper | ||||
| 
 | ||||
| def raises(expected_exception, *args, **kwargs): | ||||
|     """ assert that a code block/function call raises @expected_exception | ||||
|     """ assert that a code block/function call raises ``expected_exception`` | ||||
|     and raise a failure exception otherwise. | ||||
| 
 | ||||
|     This helper produces a ``py.code.ExceptionInfo()`` object. | ||||
|     This helper produces a ``ExceptionInfo()`` object (see below). | ||||
| 
 | ||||
|     If using Python 2.5 or above, you may use this function as a | ||||
|     context manager:: | ||||
|  | @ -1221,19 +1222,19 @@ def raises(expected_exception, *args, **kwargs): | |||
|        Lines of code after that, within the scope of the context manager will | ||||
|        not be executed. For example:: | ||||
| 
 | ||||
|            >>> with raises(OSError) as err: | ||||
|            >>> with raises(OSError) as exc_info: | ||||
|                    assert 1 == 1  # this will execute as expected | ||||
|                    raise OSError(errno.EEXISTS, 'directory exists') | ||||
|                    assert err.errno == errno.EEXISTS  # this will not execute | ||||
|                    assert exc_info.value.errno == errno.EEXISTS  # this will not execute | ||||
| 
 | ||||
|        Instead, the following approach must be taken (note the difference in | ||||
|        scope):: | ||||
| 
 | ||||
|            >>> with raises(OSError) as err: | ||||
|            >>> with raises(OSError) as exc_info: | ||||
|                    assert 1 == 1  # this will execute as expected | ||||
|                    raise OSError(errno.EEXISTS, 'directory exists') | ||||
| 
 | ||||
|                assert err.errno == errno.EEXISTS  # this will now execute | ||||
|                assert exc_info.value.errno == errno.EEXISTS  # this will now execute | ||||
| 
 | ||||
|     Or you can specify a callable by passing a to-be-called lambda:: | ||||
| 
 | ||||
|  | @ -1254,21 +1255,22 @@ def raises(expected_exception, *args, **kwargs): | |||
|         >>> raises(ZeroDivisionError, "f(0)") | ||||
|         <ExceptionInfo ...> | ||||
| 
 | ||||
|     Performance note: | ||||
|     ----------------- | ||||
|     .. autoclass:: _pytest._code.ExceptionInfo | ||||
|         :members: | ||||
| 
 | ||||
|     Similar to caught exception objects in Python, explicitly clearing | ||||
|     local references to returned ``py.code.ExceptionInfo`` objects can | ||||
|     help the Python interpreter speed up its garbage collection. | ||||
|     .. note:: | ||||
|         Similar to caught exception objects in Python, explicitly clearing | ||||
|         local references to returned ``ExceptionInfo`` objects can | ||||
|         help the Python interpreter speed up its garbage collection. | ||||
| 
 | ||||
|     Clearing those references breaks a reference cycle | ||||
|     (``ExceptionInfo`` --> caught exception --> frame stack raising | ||||
|     the exception --> current frame stack --> local variables --> | ||||
|     ``ExceptionInfo``) which makes Python keep all objects referenced | ||||
|     from that cycle (including all local variables in the current | ||||
|     frame) alive until the next cyclic garbage collection run. See the | ||||
|     official Python ``try`` statement documentation for more detailed | ||||
|     information. | ||||
|         Clearing those references breaks a reference cycle | ||||
|         (``ExceptionInfo`` --> caught exception --> frame stack raising | ||||
|         the exception --> current frame stack --> local variables --> | ||||
|         ``ExceptionInfo``) which makes Python keep all objects referenced | ||||
|         from that cycle (including all local variables in the current | ||||
|         frame) alive until the next cyclic garbage collection run. See the | ||||
|         official Python ``try`` statement documentation for more detailed | ||||
|         information. | ||||
| 
 | ||||
|     """ | ||||
|     __tracebackhide__ = True | ||||
|  | @ -1297,18 +1299,18 @@ def raises(expected_exception, *args, **kwargs): | |||
|         loc.update(kwargs) | ||||
|         #print "raises frame scope: %r" % frame.f_locals | ||||
|         try: | ||||
|             code = py.code.Source(code).compile() | ||||
|             code = _pytest._code.Source(code).compile() | ||||
|             py.builtin.exec_(code, frame.f_globals, loc) | ||||
|             # XXX didn'T mean f_globals == f_locals something special? | ||||
|             #     this is destroyed here ... | ||||
|         except expected_exception: | ||||
|             return py.code.ExceptionInfo() | ||||
|             return _pytest._code.ExceptionInfo() | ||||
|     else: | ||||
|         func = args[0] | ||||
|         try: | ||||
|             func(*args[1:], **kwargs) | ||||
|         except expected_exception: | ||||
|             return py.code.ExceptionInfo() | ||||
|             return _pytest._code.ExceptionInfo() | ||||
|     pytest.fail("DID NOT RAISE") | ||||
| 
 | ||||
| class RaisesContext(object): | ||||
|  | @ -1317,7 +1319,7 @@ class RaisesContext(object): | |||
|         self.excinfo = None | ||||
| 
 | ||||
|     def __enter__(self): | ||||
|         self.excinfo = object.__new__(py.code.ExceptionInfo) | ||||
|         self.excinfo = object.__new__(_pytest._code.ExceptionInfo) | ||||
|         return self.excinfo | ||||
| 
 | ||||
|     def __exit__(self, *tp): | ||||
|  | @ -2025,7 +2027,7 @@ class FixtureManager: | |||
| def fail_fixturefunc(fixturefunc, msg): | ||||
|     fs, lineno = getfslineno(fixturefunc) | ||||
|     location = "%s:%s" % (fs, lineno+1) | ||||
|     source = py.code.Source(fixturefunc) | ||||
|     source = _pytest._code.Source(fixturefunc) | ||||
|     pytest.fail(msg + ":\n\n" + str(source.indent()) + "\n" + location, | ||||
|                 pytrace=False) | ||||
| 
 | ||||
|  | @ -2168,14 +2170,14 @@ def getfuncargnames(function, startindex=None): | |||
|         startindex += num_mock_patch_args(function) | ||||
|         function = realfunction | ||||
|     if isinstance(function, functools.partial): | ||||
|         argnames = inspect.getargs(py.code.getrawcode(function.func))[0] | ||||
|         argnames = inspect.getargs(_pytest._code.getrawcode(function.func))[0] | ||||
|         partial = function | ||||
|         argnames = argnames[len(partial.args):] | ||||
|         if partial.keywords: | ||||
|             for kw in partial.keywords: | ||||
|                 argnames.remove(kw) | ||||
|     else: | ||||
|         argnames = inspect.getargs(py.code.getrawcode(function))[0] | ||||
|         argnames = inspect.getargs(_pytest._code.getrawcode(function))[0] | ||||
|     defaults = getattr(function, 'func_defaults', | ||||
|                        getattr(function, '__defaults__', None)) or () | ||||
|     numdefaults = len(defaults) | ||||
|  |  | |||
|  | @ -1,6 +1,8 @@ | |||
| """ recording warnings during test function execution. """ | ||||
| 
 | ||||
| import inspect | ||||
| 
 | ||||
| import _pytest._code | ||||
| import py | ||||
| import sys | ||||
| import warnings | ||||
|  | @ -100,7 +102,7 @@ def warns(expected_warning, *args, **kwargs): | |||
|         loc.update(kwargs) | ||||
| 
 | ||||
|         with wcheck: | ||||
|             code = py.code.Source(code).compile() | ||||
|             code = _pytest._code.Source(code).compile() | ||||
|             py.builtin.exec_(code, frame.f_globals, loc) | ||||
|     else: | ||||
|         func = args[0] | ||||
|  |  | |||
|  | @ -5,7 +5,8 @@ from time import time | |||
| 
 | ||||
| import py | ||||
| import pytest | ||||
| from py._code.code import TerminalRepr | ||||
| from _pytest._code.code import TerminalRepr, ExceptionInfo | ||||
| 
 | ||||
| 
 | ||||
| def pytest_namespace(): | ||||
|     return { | ||||
|  | @ -151,7 +152,7 @@ class CallInfo: | |||
|             self.stop = time() | ||||
|             raise | ||||
|         except: | ||||
|             self.excinfo = py.code.ExceptionInfo() | ||||
|             self.excinfo = ExceptionInfo() | ||||
|         self.stop = time() | ||||
| 
 | ||||
|     def __repr__(self): | ||||
|  | @ -215,7 +216,7 @@ def pytest_runtest_makereport(item, call): | |||
|         outcome = "passed" | ||||
|         longrepr = None | ||||
|     else: | ||||
|         if not isinstance(excinfo, py.code.ExceptionInfo): | ||||
|         if not isinstance(excinfo, ExceptionInfo): | ||||
|             outcome = "failed" | ||||
|             longrepr = excinfo | ||||
|         elif excinfo.errisinstance(pytest.skip.Exception): | ||||
|  |  | |||
|  | @ -291,9 +291,8 @@ def cached_eval(config, expr, d): | |||
|     try: | ||||
|         return config._evalcache[expr] | ||||
|     except KeyError: | ||||
|         #import sys | ||||
|         #print >>sys.stderr, ("cache-miss: %r" % expr) | ||||
|         exprcode = py.code.compile(expr, mode="eval") | ||||
|         import _pytest._code | ||||
|         exprcode = _pytest._code.compile(expr, mode="eval") | ||||
|         config._evalcache[expr] = x = eval(exprcode, d) | ||||
|         return x | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,13 +1,12 @@ | |||
| """ discovery and running of std-library "unittest" style tests. """ | ||||
| from __future__ import absolute_import | ||||
| import traceback | ||||
| 
 | ||||
| import sys | ||||
| import traceback | ||||
| 
 | ||||
| import pytest | ||||
| import py | ||||
| 
 | ||||
| 
 | ||||
| # for transfering markers | ||||
| import _pytest._code | ||||
| from _pytest.python import transfer_markers | ||||
| from _pytest.skipping import MarkEvaluator | ||||
| 
 | ||||
|  | @ -101,7 +100,7 @@ class TestCaseFunction(pytest.Function): | |||
|         # unwrap potential exception info (see twisted trial support below) | ||||
|         rawexcinfo = getattr(rawexcinfo, '_rawexcinfo', rawexcinfo) | ||||
|         try: | ||||
|             excinfo = py.code.ExceptionInfo(rawexcinfo) | ||||
|             excinfo = _pytest._code.ExceptionInfo(rawexcinfo) | ||||
|         except TypeError: | ||||
|             try: | ||||
|                 try: | ||||
|  | @ -117,7 +116,7 @@ class TestCaseFunction(pytest.Function): | |||
|             except KeyboardInterrupt: | ||||
|                 raise | ||||
|             except pytest.fail.Exception: | ||||
|                 excinfo = py.code.ExceptionInfo() | ||||
|                 excinfo = _pytest._code.ExceptionInfo() | ||||
|         self.__dict__.setdefault('_excinfo', []).append(excinfo) | ||||
| 
 | ||||
|     def addError(self, testcase, rawexcinfo): | ||||
|  |  | |||
|  | @ -81,13 +81,10 @@ and if you need to have access to the actual exception info you may use:: | |||
|             f() | ||||
|         assert 'maximum recursion' in str(excinfo.value) | ||||
| 
 | ||||
| ``excinfo`` is a `py.code.ExceptionInfo`_ instance, which is a wrapper around | ||||
| ``excinfo`` is a ``ExceptionInfo`` instance, which is a wrapper around | ||||
| the actual exception raised.  The main attributes of interest are | ||||
| ``.type``, ``.value`` and ``.traceback``. | ||||
| 
 | ||||
| .. _py.code.ExceptionInfo: | ||||
|     http://pylib.readthedocs.org/en/latest/code.html#py-code-exceptioninfo | ||||
| 
 | ||||
| If you want to write test code that works on Python 2.4 as well, | ||||
| you may also use two other ways to test for an expected exception:: | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,4 +1,5 @@ | |||
| from pytest import raises | ||||
| import _pytest._code | ||||
| import py | ||||
| 
 | ||||
| def otherfunc(a,b): | ||||
|  | @ -159,7 +160,7 @@ def test_dynamic_compile_shows_nicely(): | |||
|     src = 'def foo():\n assert 1 == 0\n' | ||||
|     name = 'abc-123' | ||||
|     module = py.std.imp.new_module(name) | ||||
|     code = py.code.compile(src, name, 'exec') | ||||
|     code = _pytest._code.compile(src, name, 'exec') | ||||
|     py.builtin.exec_(code, module.__dict__) | ||||
|     py.std.sys.modules[name] = module | ||||
|     module.foo() | ||||
|  |  | |||
|  | @ -4,6 +4,7 @@ serialization via the pickle module. | |||
| """ | ||||
| import py | ||||
| import pytest | ||||
| import _pytest._code | ||||
| 
 | ||||
| pythonlist = ['python2.6', 'python2.7', 'python3.3'] | ||||
| @pytest.fixture(params=pythonlist) | ||||
|  | @ -23,7 +24,7 @@ class Python: | |||
|         self.picklefile = picklefile | ||||
|     def dumps(self, obj): | ||||
|         dumpfile = self.picklefile.dirpath("dump.py") | ||||
|         dumpfile.write(py.code.Source(""" | ||||
|         dumpfile.write(_pytest._code.Source(""" | ||||
|             import pickle | ||||
|             f = open(%r, 'wb') | ||||
|             s = pickle.dump(%r, f, protocol=2) | ||||
|  | @ -33,7 +34,7 @@ class Python: | |||
| 
 | ||||
|     def load_and_is_true(self, expression): | ||||
|         loadfile = self.picklefile.dirpath("load.py") | ||||
|         loadfile.write(py.code.Source(""" | ||||
|         loadfile.write(_pytest._code.Source(""" | ||||
|             import pickle | ||||
|             f = open(%r, 'rb') | ||||
|             obj = pickle.load(f) | ||||
|  |  | |||
							
								
								
									
										2
									
								
								setup.py
								
								
								
								
							
							
						
						
									
										2
									
								
								setup.py
								
								
								
								
							|  | @ -75,7 +75,7 @@ def main(): | |||
|         # the following should be enabled for release | ||||
|         install_requires=install_requires, | ||||
|         extras_require=extras_require, | ||||
|         packages=['_pytest', '_pytest.assertion', '_pytest.vendored_packages'], | ||||
|         packages=['_pytest', '_pytest.assertion', '_pytest._code', '_pytest.vendored_packages'], | ||||
|         py_modules=['pytest'], | ||||
|         zip_safe=False, | ||||
|     ) | ||||
|  |  | |||
|  | @ -1,5 +1,8 @@ | |||
| import sys | ||||
| import py, pytest | ||||
| 
 | ||||
| import _pytest._code | ||||
| import py | ||||
| import pytest | ||||
| from _pytest.main import EXIT_NOTESTSCOLLECTED, EXIT_USAGEERROR | ||||
| 
 | ||||
| 
 | ||||
|  | @ -197,7 +200,7 @@ class TestGeneralUsage: | |||
|     def test_chdir(self, testdir): | ||||
|         testdir.tmpdir.join("py").mksymlinkto(py._pydir) | ||||
|         p = testdir.tmpdir.join("main.py") | ||||
|         p.write(py.code.Source(""" | ||||
|         p.write(_pytest._code.Source(""" | ||||
|             import sys, os | ||||
|             sys.path.insert(0, '') | ||||
|             import py | ||||
|  |  | |||
|  | @ -0,0 +1,163 @@ | |||
| import sys | ||||
| 
 | ||||
| import _pytest._code | ||||
| import py | ||||
| import pytest | ||||
| 
 | ||||
| 
 | ||||
| def test_ne(): | ||||
|     code1 = _pytest._code.Code(compile('foo = "bar"', '', 'exec')) | ||||
|     assert code1 == code1 | ||||
|     code2 = _pytest._code.Code(compile('foo = "baz"', '', 'exec')) | ||||
|     assert code2 != code1 | ||||
| 
 | ||||
| def test_code_gives_back_name_for_not_existing_file(): | ||||
|     name = 'abc-123' | ||||
|     co_code = compile("pass\n", name, 'exec') | ||||
|     assert co_code.co_filename == name | ||||
|     code = _pytest._code.Code(co_code) | ||||
|     assert str(code.path) == name | ||||
|     assert code.fullsource is None | ||||
| 
 | ||||
| def test_code_with_class(): | ||||
|     class A: | ||||
|         pass | ||||
|     pytest.raises(TypeError, "_pytest._code.Code(A)") | ||||
| 
 | ||||
| if True: | ||||
|     def x(): | ||||
|         pass | ||||
| 
 | ||||
| def test_code_fullsource(): | ||||
|     code = _pytest._code.Code(x) | ||||
|     full = code.fullsource | ||||
|     assert 'test_code_fullsource()' in str(full) | ||||
| 
 | ||||
| def test_code_source(): | ||||
|     code = _pytest._code.Code(x) | ||||
|     src = code.source() | ||||
|     expected = """def x(): | ||||
|     pass""" | ||||
|     assert str(src) == expected | ||||
| 
 | ||||
| def test_frame_getsourcelineno_myself(): | ||||
|     def func(): | ||||
|         return sys._getframe(0) | ||||
|     f = func() | ||||
|     f = _pytest._code.Frame(f) | ||||
|     source, lineno = f.code.fullsource, f.lineno | ||||
|     assert source[lineno].startswith("        return sys._getframe(0)") | ||||
| 
 | ||||
| def test_getstatement_empty_fullsource(): | ||||
|     def func(): | ||||
|         return sys._getframe(0) | ||||
|     f = func() | ||||
|     f = _pytest._code.Frame(f) | ||||
|     prop = f.code.__class__.fullsource | ||||
|     try: | ||||
|         f.code.__class__.fullsource = None | ||||
|         assert f.statement == _pytest._code.Source("") | ||||
|     finally: | ||||
|         f.code.__class__.fullsource = prop | ||||
| 
 | ||||
| def test_code_from_func(): | ||||
|     co = _pytest._code.Code(test_frame_getsourcelineno_myself) | ||||
|     assert co.firstlineno | ||||
|     assert co.path | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| def test_builtin_patch_unpatch(monkeypatch): | ||||
|     cpy_builtin = py.builtin.builtins | ||||
|     comp = cpy_builtin.compile | ||||
|     def mycompile(*args, **kwargs): | ||||
|         return comp(*args, **kwargs) | ||||
|     class Sub(AssertionError): | ||||
|         pass | ||||
|     monkeypatch.setattr(cpy_builtin, 'AssertionError', Sub) | ||||
|     monkeypatch.setattr(cpy_builtin, 'compile', mycompile) | ||||
|     _pytest._code.patch_builtins() | ||||
|     assert cpy_builtin.AssertionError != Sub | ||||
|     assert cpy_builtin.compile != mycompile | ||||
|     _pytest._code.unpatch_builtins() | ||||
|     assert cpy_builtin.AssertionError is Sub | ||||
|     assert cpy_builtin.compile == mycompile | ||||
| 
 | ||||
| 
 | ||||
| def test_unicode_handling(): | ||||
|     value = py.builtin._totext('\xc4\x85\xc4\x87\n', 'utf-8').encode('utf8') | ||||
|     def f(): | ||||
|         raise Exception(value) | ||||
|     excinfo = pytest.raises(Exception, f) | ||||
|     str(excinfo) | ||||
|     if sys.version_info[0] < 3: | ||||
|         unicode(excinfo) | ||||
| 
 | ||||
| def test_code_getargs(): | ||||
|     def f1(x): | ||||
|         pass | ||||
|     c1 = _pytest._code.Code(f1) | ||||
|     assert c1.getargs(var=True) == ('x',) | ||||
| 
 | ||||
|     def f2(x, *y): | ||||
|         pass | ||||
|     c2 = _pytest._code.Code(f2) | ||||
|     assert c2.getargs(var=True) == ('x', 'y') | ||||
| 
 | ||||
|     def f3(x, **z): | ||||
|         pass | ||||
|     c3 = _pytest._code.Code(f3) | ||||
|     assert c3.getargs(var=True) == ('x', 'z') | ||||
| 
 | ||||
|     def f4(x, *y, **z): | ||||
|         pass | ||||
|     c4 = _pytest._code.Code(f4) | ||||
|     assert c4.getargs(var=True) == ('x', 'y', 'z') | ||||
| 
 | ||||
| 
 | ||||
| def test_frame_getargs(): | ||||
|     def f1(x): | ||||
|         return sys._getframe(0) | ||||
|     fr1 = _pytest._code.Frame(f1('a')) | ||||
|     assert fr1.getargs(var=True) == [('x', 'a')] | ||||
| 
 | ||||
|     def f2(x, *y): | ||||
|         return sys._getframe(0) | ||||
|     fr2 = _pytest._code.Frame(f2('a', 'b', 'c')) | ||||
|     assert fr2.getargs(var=True) == [('x', 'a'), ('y', ('b', 'c'))] | ||||
| 
 | ||||
|     def f3(x, **z): | ||||
|         return sys._getframe(0) | ||||
|     fr3 = _pytest._code.Frame(f3('a', b='c')) | ||||
|     assert fr3.getargs(var=True) == [('x', 'a'), ('z', {'b': 'c'})] | ||||
| 
 | ||||
|     def f4(x, *y, **z): | ||||
|         return sys._getframe(0) | ||||
|     fr4 = _pytest._code.Frame(f4('a', 'b', c='d')) | ||||
|     assert fr4.getargs(var=True) == [('x', 'a'), ('y', ('b',)), | ||||
|                                      ('z', {'c': 'd'})] | ||||
| 
 | ||||
| 
 | ||||
| class TestExceptionInfo: | ||||
| 
 | ||||
|     def test_bad_getsource(self): | ||||
|         try: | ||||
|             if False: pass | ||||
|             else: assert False | ||||
|         except AssertionError: | ||||
|             exci = _pytest._code.ExceptionInfo() | ||||
|         assert exci.getrepr() | ||||
| 
 | ||||
| 
 | ||||
| class TestTracebackEntry: | ||||
| 
 | ||||
|     def test_getsource(self): | ||||
|         try: | ||||
|             if False: pass | ||||
|             else: assert False | ||||
|         except AssertionError: | ||||
|             exci = _pytest._code.ExceptionInfo() | ||||
|         entry = exci.traceback[0] | ||||
|         source = entry.getsource() | ||||
|         assert len(source) == 4 | ||||
|         assert 'else: assert False' in source[3] | ||||
|  | @ -0,0 +1,309 @@ | |||
| import pytest, py | ||||
| 
 | ||||
| def exvalue(): | ||||
|     return py.std.sys.exc_info()[1] | ||||
| 
 | ||||
| def f(): | ||||
|     return 2 | ||||
| 
 | ||||
| def test_assert(): | ||||
|     try: | ||||
|         assert f() == 3 | ||||
|     except AssertionError: | ||||
|         e = exvalue() | ||||
|         s = str(e) | ||||
|         assert s.startswith('assert 2 == 3\n') | ||||
| 
 | ||||
| 
 | ||||
| def test_assert_within_finally(): | ||||
|     excinfo = pytest.raises(ZeroDivisionError, """ | ||||
|         try: | ||||
|             1/0 | ||||
|         finally: | ||||
|             i = 42 | ||||
|     """) | ||||
|     s = excinfo.exconly() | ||||
|     assert py.std.re.search("division.+by zero", s) is not None | ||||
| 
 | ||||
|     #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 = exvalue() | ||||
|         s = str(e) | ||||
|         assert s.startswith('assert 2 == 3\n') | ||||
| 
 | ||||
| def test_assert_multiline_2(): | ||||
|     try: | ||||
|         assert (f() == (4, | ||||
|                    3)[-1]) | ||||
|     except AssertionError: | ||||
|         e = exvalue() | ||||
|         s = str(e) | ||||
|         assert s.startswith('assert 2 ==') | ||||
| 
 | ||||
| def test_in(): | ||||
|     try: | ||||
|         assert "hi" in [1, 2] | ||||
|     except AssertionError: | ||||
|         e = exvalue() | ||||
|         s = str(e) | ||||
|         assert s.startswith("assert 'hi' in") | ||||
| 
 | ||||
| def test_is(): | ||||
|     try: | ||||
|         assert 1 is 2 | ||||
|     except AssertionError: | ||||
|         e = exvalue() | ||||
|         s = str(e) | ||||
|         assert s.startswith("assert 1 is 2") | ||||
| 
 | ||||
| 
 | ||||
| @pytest.mark.skipif("sys.version_info < (2,6)") | ||||
| def test_attrib(): | ||||
|     class Foo(object): | ||||
|         b = 1 | ||||
|     i = Foo() | ||||
|     try: | ||||
|         assert i.b == 2 | ||||
|     except AssertionError: | ||||
|         e = exvalue() | ||||
|         s = str(e) | ||||
|         assert s.startswith("assert 1 == 2") | ||||
| 
 | ||||
| @pytest.mark.skipif("sys.version_info < (2,6)") | ||||
| def test_attrib_inst(): | ||||
|     class Foo(object): | ||||
|         b = 1 | ||||
|     try: | ||||
|         assert Foo().b == 2 | ||||
|     except AssertionError: | ||||
|         e = exvalue() | ||||
|         s = str(e) | ||||
|         assert s.startswith("assert 1 == 2") | ||||
| 
 | ||||
| def test_len(): | ||||
|     l = list(range(42)) | ||||
|     try: | ||||
|         assert len(l) == 100 | ||||
|     except AssertionError: | ||||
|         e = exvalue() | ||||
|         s = str(e) | ||||
|         assert s.startswith("assert 42 == 100") | ||||
|         assert "where 42 = len([" in s | ||||
| 
 | ||||
| 
 | ||||
| def test_assert_keyword_arg(): | ||||
|     def f(x=3): | ||||
|         return False | ||||
|     try: | ||||
|         assert f(x=5) | ||||
|     except AssertionError: | ||||
|         e = exvalue() | ||||
|         assert "x=5" in e.msg | ||||
| 
 | ||||
| # 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 = exvalue() | ||||
|         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 = exvalue() | ||||
|         assert e.msg.find("list") != -1 | ||||
| 
 | ||||
| def test_assert_implicit_multiline(): | ||||
|     try: | ||||
|         x = [1,2,3] | ||||
|         assert x != [1, | ||||
|            2, 3] | ||||
|     except AssertionError: | ||||
|         e = exvalue() | ||||
|         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: | ||||
|         pytest.fail("broken __repr__ not handle correctly") | ||||
| 
 | ||||
| def test_multiple_statements_per_line(): | ||||
|     try: | ||||
|         a = 1; assert a == 2 | ||||
|     except AssertionError: | ||||
|         e = exvalue() | ||||
|         assert "assert 1 == 2" in e.msg | ||||
| 
 | ||||
| def test_power(): | ||||
|     try: | ||||
|         assert 2**3 == 7 | ||||
|     except AssertionError: | ||||
|         e = exvalue() | ||||
|         assert "assert (2 ** 3) == 7" in e.msg | ||||
| 
 | ||||
| 
 | ||||
| class TestView: | ||||
| 
 | ||||
|     def setup_class(cls): | ||||
|         cls.View = pytest.importorskip("_pytest._code._assertionold").View | ||||
| 
 | ||||
|     def test_class_dispatch(self): | ||||
|         ### Use a custom class hierarchy with existing instances | ||||
| 
 | ||||
|         class Picklable(self.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(self.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_underscore_api(): | ||||
|     import _pytest._code | ||||
|     _pytest._code._AssertionError | ||||
|     _pytest._code._reinterpret_old # used by pypy | ||||
|     _pytest._code._reinterpret | ||||
| 
 | ||||
| @pytest.mark.skipif("sys.version_info < (2,6)") | ||||
| def test_assert_customizable_reprcompare(monkeypatch): | ||||
|     util = pytest.importorskip("_pytest.assertion.util") | ||||
|     monkeypatch.setattr(util, '_reprcompare', lambda *args: 'hello') | ||||
|     try: | ||||
|         assert 3 == 4 | ||||
|     except AssertionError: | ||||
|         e = exvalue() | ||||
|         s = str(e) | ||||
|         assert "hello" in s | ||||
| 
 | ||||
| def test_assert_long_source_1(): | ||||
|     try: | ||||
|         assert len == [ | ||||
|             (None, ['somet text', 'more text']), | ||||
|         ] | ||||
|     except AssertionError: | ||||
|         e = exvalue() | ||||
|         s = str(e) | ||||
|         assert 're-run' not in s | ||||
|         assert 'somet text' in s | ||||
| 
 | ||||
| def test_assert_long_source_2(): | ||||
|     try: | ||||
|         assert(len == [ | ||||
|             (None, ['somet text', 'more text']), | ||||
|         ]) | ||||
|     except AssertionError: | ||||
|         e = exvalue() | ||||
|         s = str(e) | ||||
|         assert 're-run' not in s | ||||
|         assert 'somet text' in s | ||||
| 
 | ||||
| def test_assert_raise_alias(testdir): | ||||
|     testdir.makepyfile(""" | ||||
|     import sys | ||||
|     EX = AssertionError | ||||
|     def test_hello(): | ||||
|         raise EX("hello" | ||||
|             "multi" | ||||
|             "line") | ||||
|     """) | ||||
|     result = testdir.runpytest() | ||||
|     result.stdout.fnmatch_lines([ | ||||
|         "*def test_hello*", | ||||
|         "*raise EX*", | ||||
|         "*1 failed*", | ||||
|     ]) | ||||
| 
 | ||||
| 
 | ||||
| @pytest.mark.skipif("sys.version_info < (2,5)") | ||||
| def test_assert_raise_subclass(): | ||||
|     class SomeEx(AssertionError): | ||||
|         def __init__(self, *args): | ||||
|             super(SomeEx, self).__init__() | ||||
|     try: | ||||
|         raise SomeEx("hello") | ||||
|     except AssertionError: | ||||
|         s = str(exvalue()) | ||||
|         assert 're-run' not in s | ||||
|         assert 'could not determine' in s | ||||
| 
 | ||||
| def test_assert_raises_in_nonzero_of_object_pytest_issue10(): | ||||
|     class A(object): | ||||
|         def __nonzero__(self): | ||||
|             raise ValueError(42) | ||||
|         def __lt__(self, other): | ||||
|             return A() | ||||
|         def __repr__(self): | ||||
|             return "<MY42 object>" | ||||
|     def myany(x): | ||||
|         return True | ||||
|     try: | ||||
|         assert not(myany(A() < 0)) | ||||
|     except AssertionError: | ||||
|         e = exvalue() | ||||
|         s = str(e) | ||||
|         assert "<MY42 object> < 0" in s | ||||
|  | @ -0,0 +1,911 @@ | |||
| # -*- coding: utf-8 -*- | ||||
| 
 | ||||
| import _pytest | ||||
| import py | ||||
| import pytest | ||||
| from _pytest._code.code import FormattedExcinfo, ReprExceptionInfo | ||||
| 
 | ||||
| queue = py.builtin._tryimport('queue', 'Queue') | ||||
| 
 | ||||
| failsonjython = pytest.mark.xfail("sys.platform.startswith('java')") | ||||
| from test_source import astonly | ||||
| 
 | ||||
| try: | ||||
|     import importlib | ||||
| except ImportError: | ||||
|     invalidate_import_caches = None | ||||
| else: | ||||
|     invalidate_import_caches = getattr(importlib, "invalidate_caches", None) | ||||
| 
 | ||||
| import pytest | ||||
| pytest_version_info = tuple(map(int, pytest.__version__.split(".")[:3])) | ||||
| 
 | ||||
| class TWMock: | ||||
|     def __init__(self): | ||||
|         self.lines = [] | ||||
|     def sep(self, sep, line=None): | ||||
|         self.lines.append((sep, line)) | ||||
|     def line(self, line, **kw): | ||||
|         self.lines.append(line) | ||||
|     def markup(self, text, **kw): | ||||
|         return text | ||||
| 
 | ||||
|     fullwidth = 80 | ||||
| 
 | ||||
| def test_excinfo_simple(): | ||||
|     try: | ||||
|         raise ValueError | ||||
|     except ValueError: | ||||
|         info = _pytest._code.ExceptionInfo() | ||||
|     assert info.type == ValueError | ||||
| 
 | ||||
| def test_excinfo_getstatement(): | ||||
|     def g(): | ||||
|         raise ValueError | ||||
|     def f(): | ||||
|         g() | ||||
|     try: | ||||
|         f() | ||||
|     except ValueError: | ||||
|         excinfo = _pytest._code.ExceptionInfo() | ||||
|     linenumbers = [_pytest._code.getrawcode(f).co_firstlineno - 1 + 3, | ||||
|                    _pytest._code.getrawcode(f).co_firstlineno - 1 + 1, | ||||
|                    _pytest._code.getrawcode(g).co_firstlineno - 1 + 1, ] | ||||
|     l = list(excinfo.traceback) | ||||
|     foundlinenumbers = [x.lineno for x in l] | ||||
|     assert foundlinenumbers == linenumbers | ||||
|     #for x in info: | ||||
|     #    print "%s:%d  %s" %(x.path.relto(root), x.lineno, x.statement) | ||||
|     #xxx | ||||
| 
 | ||||
| # testchain for getentries test below | ||||
| def f(): | ||||
|     # | ||||
|     raise ValueError | ||||
|     # | ||||
| def g(): | ||||
|     # | ||||
|     __tracebackhide__ = True | ||||
|     f() | ||||
|     # | ||||
| def h(): | ||||
|     # | ||||
|     g() | ||||
|     # | ||||
| 
 | ||||
| class TestTraceback_f_g_h: | ||||
|     def setup_method(self, method): | ||||
|         try: | ||||
|             h() | ||||
|         except ValueError: | ||||
|             self.excinfo = _pytest._code.ExceptionInfo() | ||||
| 
 | ||||
|     def test_traceback_entries(self): | ||||
|         tb = self.excinfo.traceback | ||||
|         entries = list(tb) | ||||
|         assert len(tb) == 4 # maybe fragile test | ||||
|         assert len(entries) == 4 # maybe fragile test | ||||
|         names = ['f', 'g', 'h'] | ||||
|         for entry in entries: | ||||
|             try: | ||||
|                 names.remove(entry.frame.code.name) | ||||
|             except ValueError: | ||||
|                 pass | ||||
|         assert not names | ||||
| 
 | ||||
|     def test_traceback_entry_getsource(self): | ||||
|         tb = self.excinfo.traceback | ||||
|         s = str(tb[-1].getsource() ) | ||||
|         assert s.startswith("def f():") | ||||
|         assert s.endswith("raise ValueError") | ||||
| 
 | ||||
|     @astonly | ||||
|     @failsonjython | ||||
|     def test_traceback_entry_getsource_in_construct(self): | ||||
|         source = _pytest._code.Source("""\ | ||||
|             def xyz(): | ||||
|                 try: | ||||
|                     raise ValueError | ||||
|                 except somenoname: | ||||
|                     pass | ||||
|             xyz() | ||||
|         """) | ||||
|         try: | ||||
|             exec (source.compile()) | ||||
|         except NameError: | ||||
|             tb = _pytest._code.ExceptionInfo().traceback | ||||
|             print (tb[-1].getsource()) | ||||
|             s = str(tb[-1].getsource()) | ||||
|             assert s.startswith("def xyz():\n    try:") | ||||
|             assert s.strip().endswith("except somenoname:") | ||||
| 
 | ||||
|     def test_traceback_cut(self): | ||||
|         co = _pytest._code.Code(f) | ||||
|         path, firstlineno = co.path, co.firstlineno | ||||
|         traceback = self.excinfo.traceback | ||||
|         newtraceback = traceback.cut(path=path, firstlineno=firstlineno) | ||||
|         assert len(newtraceback) == 1 | ||||
|         newtraceback = traceback.cut(path=path, lineno=firstlineno+2) | ||||
|         assert len(newtraceback) == 1 | ||||
| 
 | ||||
|     def test_traceback_cut_excludepath(self, testdir): | ||||
|         p = testdir.makepyfile("def f(): raise ValueError") | ||||
|         excinfo = pytest.raises(ValueError, "p.pyimport().f()") | ||||
|         basedir = py.path.local(pytest.__file__).dirpath() | ||||
|         newtraceback = excinfo.traceback.cut(excludepath=basedir) | ||||
|         for x in newtraceback: | ||||
|             if hasattr(x, 'path'): | ||||
|                 assert not py.path.local(x.path).relto(basedir) | ||||
|         assert newtraceback[-1].frame.code.path == p | ||||
| 
 | ||||
|     def test_traceback_filter(self): | ||||
|         traceback = self.excinfo.traceback | ||||
|         ntraceback = traceback.filter() | ||||
|         assert len(ntraceback) == len(traceback) - 1 | ||||
| 
 | ||||
|     def test_traceback_recursion_index(self): | ||||
|         def f(n): | ||||
|             if n < 10: | ||||
|                 n += 1 | ||||
|             f(n) | ||||
|         excinfo = pytest.raises(RuntimeError, f, 8) | ||||
|         traceback = excinfo.traceback | ||||
|         recindex = traceback.recursionindex() | ||||
|         assert recindex == 3 | ||||
| 
 | ||||
|     def test_traceback_only_specific_recursion_errors(self, monkeypatch): | ||||
|         def f(n): | ||||
|             if n == 0: | ||||
|                 raise RuntimeError("hello") | ||||
|             f(n-1) | ||||
| 
 | ||||
|         excinfo = pytest.raises(RuntimeError, f, 100) | ||||
|         monkeypatch.delattr(excinfo.traceback.__class__, "recursionindex") | ||||
|         repr = excinfo.getrepr() | ||||
|         assert "RuntimeError: hello" in str(repr.reprcrash) | ||||
| 
 | ||||
|     def test_traceback_no_recursion_index(self): | ||||
|         def do_stuff(): | ||||
|             raise RuntimeError | ||||
|         def reraise_me(): | ||||
|             import sys | ||||
|             exc, val, tb = sys.exc_info() | ||||
|             py.builtin._reraise(exc, val, tb) | ||||
|         def f(n): | ||||
|             try: | ||||
|                 do_stuff() | ||||
|             except: | ||||
|                 reraise_me() | ||||
|         excinfo = pytest.raises(RuntimeError, f, 8) | ||||
|         traceback = excinfo.traceback | ||||
|         recindex = traceback.recursionindex() | ||||
|         assert recindex is None | ||||
| 
 | ||||
|     def test_traceback_messy_recursion(self): | ||||
|         #XXX: simplified locally testable version | ||||
|         decorator = pytest.importorskip('decorator').decorator | ||||
| 
 | ||||
|         def log(f, *k, **kw): | ||||
|             print('%s %s' % (k, kw)) | ||||
|             f(*k, **kw) | ||||
|         log = decorator(log) | ||||
| 
 | ||||
|         def fail(): | ||||
|             raise ValueError('') | ||||
| 
 | ||||
|         fail = log(log(fail)) | ||||
| 
 | ||||
|         excinfo = pytest.raises(ValueError, fail) | ||||
|         assert excinfo.traceback.recursionindex() is None | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|     def test_traceback_getcrashentry(self): | ||||
|         def i(): | ||||
|             __tracebackhide__ = True | ||||
|             raise ValueError | ||||
|         def h(): | ||||
|             i() | ||||
|         def g(): | ||||
|             __tracebackhide__ = True | ||||
|             h() | ||||
|         def f(): | ||||
|             g() | ||||
| 
 | ||||
|         excinfo = pytest.raises(ValueError, f) | ||||
|         tb = excinfo.traceback | ||||
|         entry = tb.getcrashentry() | ||||
|         co = _pytest._code.Code(h) | ||||
|         assert entry.frame.code.path == co.path | ||||
|         assert entry.lineno == co.firstlineno + 1 | ||||
|         assert entry.frame.code.name == 'h' | ||||
| 
 | ||||
|     def test_traceback_getcrashentry_empty(self): | ||||
|         def g(): | ||||
|             __tracebackhide__ = True | ||||
|             raise ValueError | ||||
|         def f(): | ||||
|             __tracebackhide__ = True | ||||
|             g() | ||||
| 
 | ||||
|         excinfo = pytest.raises(ValueError, f) | ||||
|         tb = excinfo.traceback | ||||
|         entry = tb.getcrashentry() | ||||
|         co = _pytest._code.Code(g) | ||||
|         assert entry.frame.code.path == co.path | ||||
|         assert entry.lineno == co.firstlineno + 2 | ||||
|         assert entry.frame.code.name == 'g' | ||||
| 
 | ||||
| def hello(x): | ||||
|     x + 5 | ||||
| 
 | ||||
| def test_tbentry_reinterpret(): | ||||
|     try: | ||||
|         hello("hello") | ||||
|     except TypeError: | ||||
|         excinfo = _pytest._code.ExceptionInfo() | ||||
|     tbentry = excinfo.traceback[-1] | ||||
|     msg = tbentry.reinterpret() | ||||
|     assert msg.startswith("TypeError: ('hello' + 5)") | ||||
| 
 | ||||
| def test_excinfo_exconly(): | ||||
|     excinfo = pytest.raises(ValueError, h) | ||||
|     assert excinfo.exconly().startswith('ValueError') | ||||
|     excinfo = pytest.raises(ValueError, | ||||
|         "raise ValueError('hello\\nworld')") | ||||
|     msg = excinfo.exconly(tryshort=True) | ||||
|     assert msg.startswith('ValueError') | ||||
|     assert msg.endswith("world") | ||||
| 
 | ||||
| def test_excinfo_repr(): | ||||
|     excinfo = pytest.raises(ValueError, h) | ||||
|     s = repr(excinfo) | ||||
|     assert s == "<ExceptionInfo ValueError tblen=4>" | ||||
| 
 | ||||
| def test_excinfo_str(): | ||||
|     excinfo = pytest.raises(ValueError, h) | ||||
|     s = str(excinfo) | ||||
|     assert s.startswith(__file__[:-9]) # pyc file and $py.class | ||||
|     assert s.endswith("ValueError") | ||||
|     assert len(s.split(":")) >= 3 # on windows it's 4 | ||||
| 
 | ||||
| def test_excinfo_errisinstance(): | ||||
|     excinfo = pytest.raises(ValueError, h) | ||||
|     assert excinfo.errisinstance(ValueError) | ||||
| 
 | ||||
| def test_excinfo_no_sourcecode(): | ||||
|     try: | ||||
|         exec ("raise ValueError()") | ||||
|     except ValueError: | ||||
|         excinfo = _pytest._code.ExceptionInfo() | ||||
|     s = str(excinfo.traceback[-1]) | ||||
|     if py.std.sys.version_info < (2,5): | ||||
|         assert s == "  File '<string>':1 in ?\n  ???\n" | ||||
|     else: | ||||
|         assert s == "  File '<string>':1 in <module>\n  ???\n" | ||||
| 
 | ||||
| def test_excinfo_no_python_sourcecode(tmpdir): | ||||
|     #XXX: simplified locally testable version | ||||
|     tmpdir.join('test.txt').write("{{ h()}}:") | ||||
| 
 | ||||
|     jinja2 = pytest.importorskip('jinja2') | ||||
|     loader = jinja2.FileSystemLoader(str(tmpdir)) | ||||
|     env = jinja2.Environment(loader=loader) | ||||
|     template = env.get_template('test.txt') | ||||
|     excinfo = pytest.raises(ValueError, | ||||
|                              template.render, h=h) | ||||
|     for item in excinfo.traceback: | ||||
|         print(item) #XXX: for some reason jinja.Template.render is printed in full | ||||
|         item.source # shouldnt fail | ||||
|         if item.path.basename == 'test.txt': | ||||
|             assert str(item.source) == '{{ h()}}:' | ||||
| 
 | ||||
| 
 | ||||
| def test_entrysource_Queue_example(): | ||||
|     try: | ||||
|         queue.Queue().get(timeout=0.001) | ||||
|     except queue.Empty: | ||||
|         excinfo = _pytest._code.ExceptionInfo() | ||||
|     entry = excinfo.traceback[-1] | ||||
|     source = entry.getsource() | ||||
|     assert source is not None | ||||
|     s = str(source).strip() | ||||
|     assert s.startswith("def get") | ||||
| 
 | ||||
| def test_codepath_Queue_example(): | ||||
|     try: | ||||
|         queue.Queue().get(timeout=0.001) | ||||
|     except queue.Empty: | ||||
|         excinfo = _pytest._code.ExceptionInfo() | ||||
|     entry = excinfo.traceback[-1] | ||||
|     path = entry.path | ||||
|     assert isinstance(path, py.path.local) | ||||
|     assert path.basename.lower() == "queue.py" | ||||
|     assert path.check() | ||||
| 
 | ||||
| class TestFormattedExcinfo: | ||||
|     def pytest_funcarg__importasmod(self, request): | ||||
|         def importasmod(source): | ||||
|             source = _pytest._code.Source(source) | ||||
|             tmpdir = request.getfuncargvalue("tmpdir") | ||||
|             modpath = tmpdir.join("mod.py") | ||||
|             tmpdir.ensure("__init__.py") | ||||
|             modpath.write(source) | ||||
|             if invalidate_import_caches is not None: | ||||
|                 invalidate_import_caches() | ||||
|             return modpath.pyimport() | ||||
|         return importasmod | ||||
| 
 | ||||
|     def excinfo_from_exec(self, source): | ||||
|         source = _pytest._code.Source(source).strip() | ||||
|         try: | ||||
|             exec (source.compile()) | ||||
|         except KeyboardInterrupt: | ||||
|             raise | ||||
|         except: | ||||
|             return _pytest._code.ExceptionInfo() | ||||
|         assert 0, "did not raise" | ||||
| 
 | ||||
|     def test_repr_source(self): | ||||
|         pr = FormattedExcinfo() | ||||
|         source = _pytest._code.Source(""" | ||||
|             def f(x): | ||||
|                 pass | ||||
|         """).strip() | ||||
|         pr.flow_marker = "|" | ||||
|         lines = pr.get_source(source, 0) | ||||
|         assert len(lines) == 2 | ||||
|         assert lines[0] == "|   def f(x):" | ||||
|         assert lines[1] == "        pass" | ||||
| 
 | ||||
|     def test_repr_source_excinfo(self): | ||||
|         """ check if indentation is right """ | ||||
|         pr = FormattedExcinfo() | ||||
|         excinfo = self.excinfo_from_exec(""" | ||||
|                 def f(): | ||||
|                     assert 0 | ||||
|                 f() | ||||
|         """) | ||||
|         pr = FormattedExcinfo() | ||||
|         source = pr._getentrysource(excinfo.traceback[-1]) | ||||
|         lines = pr.get_source(source, 1, excinfo) | ||||
|         assert lines == [ | ||||
|             '    def f():', | ||||
|             '>       assert 0', | ||||
|             'E       assert 0' | ||||
|         ] | ||||
| 
 | ||||
| 
 | ||||
|     def test_repr_source_not_existing(self): | ||||
|         pr = FormattedExcinfo() | ||||
|         co = compile("raise ValueError()", "", "exec") | ||||
|         try: | ||||
|             exec (co) | ||||
|         except ValueError: | ||||
|             excinfo = _pytest._code.ExceptionInfo() | ||||
|         repr = pr.repr_excinfo(excinfo) | ||||
|         assert repr.reprtraceback.reprentries[1].lines[0] == ">   ???" | ||||
| 
 | ||||
|     def test_repr_many_line_source_not_existing(self): | ||||
|         pr = FormattedExcinfo() | ||||
|         co = compile(""" | ||||
| a = 1 | ||||
| raise ValueError() | ||||
| """, "", "exec") | ||||
|         try: | ||||
|             exec (co) | ||||
|         except ValueError: | ||||
|             excinfo = _pytest._code.ExceptionInfo() | ||||
|         repr = pr.repr_excinfo(excinfo) | ||||
|         assert repr.reprtraceback.reprentries[1].lines[0] == ">   ???" | ||||
| 
 | ||||
|     def test_repr_source_failing_fullsource(self): | ||||
|         pr = FormattedExcinfo() | ||||
| 
 | ||||
|         class FakeCode(object): | ||||
|             class raw: | ||||
|                 co_filename = '?' | ||||
|             path = '?' | ||||
|             firstlineno = 5 | ||||
| 
 | ||||
|             def fullsource(self): | ||||
|                 return None | ||||
|             fullsource = property(fullsource) | ||||
| 
 | ||||
|         class FakeFrame(object): | ||||
|             code = FakeCode() | ||||
|             f_locals = {} | ||||
|             f_globals = {} | ||||
| 
 | ||||
|         class FakeTracebackEntry(_pytest._code.Traceback.Entry): | ||||
|             def __init__(self, tb): | ||||
|                 self.lineno = 5+3 | ||||
| 
 | ||||
|             @property | ||||
|             def frame(self): | ||||
|                 return FakeFrame() | ||||
| 
 | ||||
|         class Traceback(_pytest._code.Traceback): | ||||
|             Entry = FakeTracebackEntry | ||||
| 
 | ||||
|         class FakeExcinfo(_pytest._code.ExceptionInfo): | ||||
|             typename = "Foo" | ||||
|             def __init__(self): | ||||
|                 pass | ||||
| 
 | ||||
|             def exconly(self, tryshort): | ||||
|                 return "EXC" | ||||
|             def errisinstance(self, cls): | ||||
|                 return False | ||||
| 
 | ||||
|         excinfo = FakeExcinfo() | ||||
|         class FakeRawTB(object): | ||||
|             tb_next = None | ||||
|         tb = FakeRawTB() | ||||
|         excinfo.traceback = Traceback(tb) | ||||
| 
 | ||||
|         fail = IOError()  # noqa | ||||
|         repr = pr.repr_excinfo(excinfo) | ||||
|         assert repr.reprtraceback.reprentries[0].lines[0] == ">   ???" | ||||
| 
 | ||||
|         fail = py.error.ENOENT  # noqa | ||||
|         repr = pr.repr_excinfo(excinfo) | ||||
|         assert repr.reprtraceback.reprentries[0].lines[0] == ">   ???" | ||||
| 
 | ||||
| 
 | ||||
|     def test_repr_local(self): | ||||
|         p = FormattedExcinfo(showlocals=True) | ||||
|         loc = {'y': 5, 'z': 7, 'x': 3, '@x': 2, '__builtins__': {}} | ||||
|         reprlocals = p.repr_locals(loc) | ||||
|         assert reprlocals.lines | ||||
|         assert reprlocals.lines[0] == '__builtins__ = <builtins>' | ||||
|         assert reprlocals.lines[1] == 'x          = 3' | ||||
|         assert reprlocals.lines[2] == 'y          = 5' | ||||
|         assert reprlocals.lines[3] == 'z          = 7' | ||||
| 
 | ||||
|     def test_repr_tracebackentry_lines(self, importasmod): | ||||
|         mod = importasmod(""" | ||||
|             def func1(): | ||||
|                 raise ValueError("hello\\nworld") | ||||
|         """) | ||||
|         excinfo = pytest.raises(ValueError, mod.func1) | ||||
|         excinfo.traceback = excinfo.traceback.filter() | ||||
|         p = FormattedExcinfo() | ||||
|         reprtb = p.repr_traceback_entry(excinfo.traceback[-1]) | ||||
| 
 | ||||
|         # test as intermittent entry | ||||
|         lines = reprtb.lines | ||||
|         assert lines[0] == '    def func1():' | ||||
|         assert lines[1] == '>       raise ValueError("hello\\nworld")' | ||||
| 
 | ||||
|         # test as last entry | ||||
|         p = FormattedExcinfo(showlocals=True) | ||||
|         repr_entry = p.repr_traceback_entry(excinfo.traceback[-1], excinfo) | ||||
|         lines = repr_entry.lines | ||||
|         assert lines[0] == '    def func1():' | ||||
|         assert lines[1] == '>       raise ValueError("hello\\nworld")' | ||||
|         assert lines[2] == 'E       ValueError: hello' | ||||
|         assert lines[3] == 'E       world' | ||||
|         assert not lines[4:] | ||||
| 
 | ||||
|         loc = repr_entry.reprlocals is not None | ||||
|         loc = repr_entry.reprfileloc | ||||
|         assert loc.path == mod.__file__ | ||||
|         assert loc.lineno == 3 | ||||
|         #assert loc.message == "ValueError: hello" | ||||
| 
 | ||||
|     def test_repr_tracebackentry_lines2(self, importasmod): | ||||
|         mod = importasmod(""" | ||||
|             def func1(m, x, y, z): | ||||
|                 raise ValueError("hello\\nworld") | ||||
|         """) | ||||
|         excinfo = pytest.raises(ValueError, mod.func1, "m"*90, 5, 13, "z"*120) | ||||
|         excinfo.traceback = excinfo.traceback.filter() | ||||
|         entry = excinfo.traceback[-1] | ||||
|         p = FormattedExcinfo(funcargs=True) | ||||
|         reprfuncargs = p.repr_args(entry) | ||||
|         assert reprfuncargs.args[0] == ('m', repr("m"*90)) | ||||
|         assert reprfuncargs.args[1] == ('x', '5') | ||||
|         assert reprfuncargs.args[2] == ('y', '13') | ||||
|         assert reprfuncargs.args[3] == ('z', repr("z" * 120)) | ||||
| 
 | ||||
|         p = FormattedExcinfo(funcargs=True) | ||||
|         repr_entry = p.repr_traceback_entry(entry) | ||||
|         assert repr_entry.reprfuncargs.args == reprfuncargs.args | ||||
|         tw = TWMock() | ||||
|         repr_entry.toterminal(tw) | ||||
|         assert tw.lines[0] == "m = " + repr('m' * 90) | ||||
|         assert tw.lines[1] == "x = 5, y = 13" | ||||
|         assert tw.lines[2] == "z = " + repr('z' * 120) | ||||
| 
 | ||||
|     def test_repr_tracebackentry_lines_var_kw_args(self, importasmod): | ||||
|         mod = importasmod(""" | ||||
|             def func1(x, *y, **z): | ||||
|                 raise ValueError("hello\\nworld") | ||||
|         """) | ||||
|         excinfo = pytest.raises(ValueError, mod.func1, 'a', 'b', c='d') | ||||
|         excinfo.traceback = excinfo.traceback.filter() | ||||
|         entry = excinfo.traceback[-1] | ||||
|         p = FormattedExcinfo(funcargs=True) | ||||
|         reprfuncargs = p.repr_args(entry) | ||||
|         assert reprfuncargs.args[0] == ('x', repr('a')) | ||||
|         assert reprfuncargs.args[1] == ('y', repr(('b',))) | ||||
|         assert reprfuncargs.args[2] == ('z', repr({'c': 'd'})) | ||||
| 
 | ||||
|         p = FormattedExcinfo(funcargs=True) | ||||
|         repr_entry = p.repr_traceback_entry(entry) | ||||
|         assert repr_entry.reprfuncargs.args == reprfuncargs.args | ||||
|         tw = TWMock() | ||||
|         repr_entry.toterminal(tw) | ||||
|         assert tw.lines[0] == "x = 'a', y = ('b',), z = {'c': 'd'}" | ||||
| 
 | ||||
|     def test_repr_tracebackentry_short(self, importasmod): | ||||
|         mod = importasmod(""" | ||||
|             def func1(): | ||||
|                 raise ValueError("hello") | ||||
|             def entry(): | ||||
|                 func1() | ||||
|         """) | ||||
|         excinfo = pytest.raises(ValueError, mod.entry) | ||||
|         p = FormattedExcinfo(style="short") | ||||
|         reprtb = p.repr_traceback_entry(excinfo.traceback[-2]) | ||||
|         lines = reprtb.lines | ||||
|         basename = py.path.local(mod.__file__).basename | ||||
|         assert lines[0] == '    func1()' | ||||
|         assert basename in str(reprtb.reprfileloc.path) | ||||
|         assert reprtb.reprfileloc.lineno == 5 | ||||
| 
 | ||||
|         # test last entry | ||||
|         p = FormattedExcinfo(style="short") | ||||
|         reprtb = p.repr_traceback_entry(excinfo.traceback[-1], excinfo) | ||||
|         lines = reprtb.lines | ||||
|         assert lines[0] == '    raise ValueError("hello")' | ||||
|         assert lines[1] == 'E   ValueError: hello' | ||||
|         assert basename in str(reprtb.reprfileloc.path) | ||||
|         assert reprtb.reprfileloc.lineno == 3 | ||||
| 
 | ||||
|     def test_repr_tracebackentry_no(self, importasmod): | ||||
|         mod = importasmod(""" | ||||
|             def func1(): | ||||
|                 raise ValueError("hello") | ||||
|             def entry(): | ||||
|                 func1() | ||||
|         """) | ||||
|         excinfo = pytest.raises(ValueError, mod.entry) | ||||
|         p = FormattedExcinfo(style="no") | ||||
|         p.repr_traceback_entry(excinfo.traceback[-2]) | ||||
| 
 | ||||
|         p = FormattedExcinfo(style="no") | ||||
|         reprentry = p.repr_traceback_entry(excinfo.traceback[-1], excinfo) | ||||
|         lines = reprentry.lines | ||||
|         assert lines[0] == 'E   ValueError: hello' | ||||
|         assert not lines[1:] | ||||
| 
 | ||||
|     def test_repr_traceback_tbfilter(self, importasmod): | ||||
|         mod = importasmod(""" | ||||
|             def f(x): | ||||
|                 raise ValueError(x) | ||||
|             def entry(): | ||||
|                 f(0) | ||||
|         """) | ||||
|         excinfo = pytest.raises(ValueError, mod.entry) | ||||
|         p = FormattedExcinfo(tbfilter=True) | ||||
|         reprtb = p.repr_traceback(excinfo) | ||||
|         assert len(reprtb.reprentries) == 2 | ||||
|         p = FormattedExcinfo(tbfilter=False) | ||||
|         reprtb = p.repr_traceback(excinfo) | ||||
|         assert len(reprtb.reprentries) == 3 | ||||
| 
 | ||||
|     def test_traceback_short_no_source(self, importasmod, monkeypatch): | ||||
|         mod = importasmod(""" | ||||
|             def func1(): | ||||
|                 raise ValueError("hello") | ||||
|             def entry(): | ||||
|                 func1() | ||||
|         """) | ||||
|         excinfo = pytest.raises(ValueError, mod.entry) | ||||
|         from _pytest._code.code import Code | ||||
|         monkeypatch.setattr(Code, 'path', 'bogus') | ||||
|         excinfo.traceback[0].frame.code.path = "bogus" | ||||
|         p = FormattedExcinfo(style="short") | ||||
|         reprtb = p.repr_traceback_entry(excinfo.traceback[-2]) | ||||
|         lines = reprtb.lines | ||||
|         last_p = FormattedExcinfo(style="short") | ||||
|         last_reprtb = last_p.repr_traceback_entry(excinfo.traceback[-1], excinfo) | ||||
|         last_lines = last_reprtb.lines | ||||
|         monkeypatch.undo() | ||||
|         assert lines[0] == '    func1()' | ||||
| 
 | ||||
|         assert last_lines[0] == '    raise ValueError("hello")' | ||||
|         assert last_lines[1] == 'E   ValueError: hello' | ||||
| 
 | ||||
|     def test_repr_traceback_and_excinfo(self, importasmod): | ||||
|         mod = importasmod(""" | ||||
|             def f(x): | ||||
|                 raise ValueError(x) | ||||
|             def entry(): | ||||
|                 f(0) | ||||
|         """) | ||||
|         excinfo = pytest.raises(ValueError, mod.entry) | ||||
| 
 | ||||
|         for style in ("long", "short"): | ||||
|             p = FormattedExcinfo(style=style) | ||||
|             reprtb = p.repr_traceback(excinfo) | ||||
|             assert len(reprtb.reprentries) == 2 | ||||
|             assert reprtb.style == style | ||||
|             assert not reprtb.extraline | ||||
|             repr = p.repr_excinfo(excinfo) | ||||
|             assert repr.reprtraceback | ||||
|             assert len(repr.reprtraceback.reprentries) == len(reprtb.reprentries) | ||||
|             assert repr.reprcrash.path.endswith("mod.py") | ||||
|             assert repr.reprcrash.message == "ValueError: 0" | ||||
| 
 | ||||
|     def test_repr_traceback_with_invalid_cwd(self, importasmod, monkeypatch): | ||||
|         mod = importasmod(""" | ||||
|             def f(x): | ||||
|                 raise ValueError(x) | ||||
|             def entry(): | ||||
|                 f(0) | ||||
|         """) | ||||
|         excinfo = pytest.raises(ValueError, mod.entry) | ||||
| 
 | ||||
|         p = FormattedExcinfo() | ||||
|         def raiseos(): | ||||
|             raise OSError(2) | ||||
|         monkeypatch.setattr(py.std.os, 'getcwd', raiseos) | ||||
|         assert p._makepath(__file__) == __file__ | ||||
|         p.repr_traceback(excinfo) | ||||
| 
 | ||||
|     def test_repr_excinfo_addouterr(self, importasmod): | ||||
|         mod = importasmod(""" | ||||
|             def entry(): | ||||
|                 raise ValueError() | ||||
|         """) | ||||
|         excinfo = pytest.raises(ValueError, mod.entry) | ||||
|         repr = excinfo.getrepr() | ||||
|         repr.addsection("title", "content") | ||||
|         twmock = TWMock() | ||||
|         repr.toterminal(twmock) | ||||
|         assert twmock.lines[-1] == "content" | ||||
|         assert twmock.lines[-2] == ("-", "title") | ||||
| 
 | ||||
|     def test_repr_excinfo_reprcrash(self, importasmod): | ||||
|         mod = importasmod(""" | ||||
|             def entry(): | ||||
|                 raise ValueError() | ||||
|         """) | ||||
|         excinfo = pytest.raises(ValueError, mod.entry) | ||||
|         repr = excinfo.getrepr() | ||||
|         assert repr.reprcrash.path.endswith("mod.py") | ||||
|         assert repr.reprcrash.lineno == 3 | ||||
|         assert repr.reprcrash.message == "ValueError" | ||||
|         assert str(repr.reprcrash).endswith("mod.py:3: ValueError") | ||||
| 
 | ||||
|     def test_repr_traceback_recursion(self, importasmod): | ||||
|         mod = importasmod(""" | ||||
|             def rec2(x): | ||||
|                 return rec1(x+1) | ||||
|             def rec1(x): | ||||
|                 return rec2(x-1) | ||||
|             def entry(): | ||||
|                 rec1(42) | ||||
|         """) | ||||
|         excinfo = pytest.raises(RuntimeError, mod.entry) | ||||
| 
 | ||||
|         for style in ("short", "long", "no"): | ||||
|             p = FormattedExcinfo(style="short") | ||||
|             reprtb = p.repr_traceback(excinfo) | ||||
|             assert reprtb.extraline == "!!! Recursion detected (same locals & position)" | ||||
|             assert str(reprtb) | ||||
| 
 | ||||
|     def test_tb_entry_AssertionError(self, importasmod): | ||||
|         # probably this test is a bit redundant | ||||
|         # as py/magic/testing/test_assertion.py | ||||
|         # already tests correctness of | ||||
|         # assertion-reinterpretation  logic | ||||
|         mod = importasmod(""" | ||||
|             def somefunc(): | ||||
|                 x = 1 | ||||
|                 assert x == 2 | ||||
|         """) | ||||
|         excinfo = pytest.raises(AssertionError, mod.somefunc) | ||||
| 
 | ||||
|         p = FormattedExcinfo() | ||||
|         reprentry = p.repr_traceback_entry(excinfo.traceback[-1], excinfo) | ||||
|         lines = reprentry.lines | ||||
|         assert lines[-1] == "E       assert 1 == 2" | ||||
| 
 | ||||
|     def test_reprexcinfo_getrepr(self, importasmod): | ||||
|         mod = importasmod(""" | ||||
|             def f(x): | ||||
|                 raise ValueError(x) | ||||
|             def entry(): | ||||
|                 f(0) | ||||
|         """) | ||||
|         excinfo = pytest.raises(ValueError, mod.entry) | ||||
| 
 | ||||
|         for style in ("short", "long", "no"): | ||||
|             for showlocals in (True, False): | ||||
|                 repr = excinfo.getrepr(style=style, showlocals=showlocals) | ||||
|                 assert isinstance(repr, ReprExceptionInfo) | ||||
|                 assert repr.reprtraceback.style == style | ||||
| 
 | ||||
|     def test_reprexcinfo_unicode(self): | ||||
|         from _pytest._code.code import TerminalRepr | ||||
|         class MyRepr(TerminalRepr): | ||||
|             def toterminal(self, tw): | ||||
|                 tw.line(py.builtin._totext("я", "utf-8")) | ||||
|         x = py.builtin._totext(MyRepr()) | ||||
|         assert x == py.builtin._totext("я", "utf-8") | ||||
| 
 | ||||
|     def test_toterminal_long(self, importasmod): | ||||
|         mod = importasmod(""" | ||||
|             def g(x): | ||||
|                 raise ValueError(x) | ||||
|             def f(): | ||||
|                 g(3) | ||||
|         """) | ||||
|         excinfo = pytest.raises(ValueError, mod.f) | ||||
|         excinfo.traceback = excinfo.traceback.filter() | ||||
|         repr = excinfo.getrepr() | ||||
|         tw = TWMock() | ||||
|         repr.toterminal(tw) | ||||
|         assert tw.lines[0] == "" | ||||
|         tw.lines.pop(0) | ||||
|         assert tw.lines[0] == "    def f():" | ||||
|         assert tw.lines[1] == ">       g(3)" | ||||
|         assert tw.lines[2] == "" | ||||
|         assert tw.lines[3].endswith("mod.py:5: ") | ||||
|         assert tw.lines[4] == ("_ ", None) | ||||
|         assert tw.lines[5] == "" | ||||
|         assert tw.lines[6] == "    def g(x):" | ||||
|         assert tw.lines[7] == ">       raise ValueError(x)" | ||||
|         assert tw.lines[8] == "E       ValueError: 3" | ||||
|         assert tw.lines[9] == "" | ||||
|         assert tw.lines[10].endswith("mod.py:3: ValueError") | ||||
| 
 | ||||
|     def test_toterminal_long_missing_source(self, importasmod, tmpdir): | ||||
|         mod = importasmod(""" | ||||
|             def g(x): | ||||
|                 raise ValueError(x) | ||||
|             def f(): | ||||
|                 g(3) | ||||
|         """) | ||||
|         excinfo = pytest.raises(ValueError, mod.f) | ||||
|         tmpdir.join('mod.py').remove() | ||||
|         excinfo.traceback = excinfo.traceback.filter() | ||||
|         repr = excinfo.getrepr() | ||||
|         tw = TWMock() | ||||
|         repr.toterminal(tw) | ||||
|         assert tw.lines[0] == "" | ||||
|         tw.lines.pop(0) | ||||
|         assert tw.lines[0] == ">   ???" | ||||
|         assert tw.lines[1] == "" | ||||
|         assert tw.lines[2].endswith("mod.py:5: ") | ||||
|         assert tw.lines[3] == ("_ ", None) | ||||
|         assert tw.lines[4] == "" | ||||
|         assert tw.lines[5] == ">   ???" | ||||
|         assert tw.lines[6] == "E   ValueError: 3" | ||||
|         assert tw.lines[7] == "" | ||||
|         assert tw.lines[8].endswith("mod.py:3: ValueError") | ||||
| 
 | ||||
|     def test_toterminal_long_incomplete_source(self, importasmod, tmpdir): | ||||
|         mod = importasmod(""" | ||||
|             def g(x): | ||||
|                 raise ValueError(x) | ||||
|             def f(): | ||||
|                 g(3) | ||||
|         """) | ||||
|         excinfo = pytest.raises(ValueError, mod.f) | ||||
|         tmpdir.join('mod.py').write('asdf') | ||||
|         excinfo.traceback = excinfo.traceback.filter() | ||||
|         repr = excinfo.getrepr() | ||||
|         tw = TWMock() | ||||
|         repr.toterminal(tw) | ||||
|         assert tw.lines[0] == "" | ||||
|         tw.lines.pop(0) | ||||
|         assert tw.lines[0] == ">   ???" | ||||
|         assert tw.lines[1] == "" | ||||
|         assert tw.lines[2].endswith("mod.py:5: ") | ||||
|         assert tw.lines[3] == ("_ ", None) | ||||
|         assert tw.lines[4] == "" | ||||
|         assert tw.lines[5] == ">   ???" | ||||
|         assert tw.lines[6] == "E   ValueError: 3" | ||||
|         assert tw.lines[7] == "" | ||||
|         assert tw.lines[8].endswith("mod.py:3: ValueError") | ||||
| 
 | ||||
|     def test_toterminal_long_filenames(self, importasmod): | ||||
|         mod = importasmod(""" | ||||
|             def f(): | ||||
|                 raise ValueError() | ||||
|         """) | ||||
|         excinfo = pytest.raises(ValueError, mod.f) | ||||
|         tw = TWMock() | ||||
|         path = py.path.local(mod.__file__) | ||||
|         old = path.dirpath().chdir() | ||||
|         try: | ||||
|             repr = excinfo.getrepr(abspath=False) | ||||
|             repr.toterminal(tw) | ||||
|             line = tw.lines[-1] | ||||
|             x = py.path.local().bestrelpath(path) | ||||
|             if len(x) < len(str(path)): | ||||
|                 assert line == "mod.py:3: ValueError" | ||||
| 
 | ||||
|             repr = excinfo.getrepr(abspath=True) | ||||
|             repr.toterminal(tw) | ||||
|             line = tw.lines[-1] | ||||
|             assert line == "%s:3: ValueError" %(path,) | ||||
|         finally: | ||||
|             old.chdir() | ||||
| 
 | ||||
|     @pytest.mark.parametrize('reproptions', [ | ||||
|         {'style': style, 'showlocals': showlocals, | ||||
|          'funcargs': funcargs, 'tbfilter': tbfilter | ||||
|         } for style in ("long", "short", "no") | ||||
|             for showlocals in (True, False) | ||||
|                 for tbfilter in (True, False) | ||||
|                     for funcargs in (True, False)]) | ||||
|     def test_format_excinfo(self, importasmod, reproptions): | ||||
|         mod = importasmod(""" | ||||
|             def g(x): | ||||
|                 raise ValueError(x) | ||||
|             def f(): | ||||
|                 g(3) | ||||
|         """) | ||||
|         excinfo = pytest.raises(ValueError, mod.f) | ||||
|         tw = py.io.TerminalWriter(stringio=True) | ||||
|         repr = excinfo.getrepr(**reproptions) | ||||
|         repr.toterminal(tw) | ||||
|         assert tw.stringio.getvalue() | ||||
| 
 | ||||
| 
 | ||||
|     def test_native_style(self): | ||||
|         excinfo = self.excinfo_from_exec(""" | ||||
|             assert 0 | ||||
|         """) | ||||
|         repr = excinfo.getrepr(style='native') | ||||
|         assert "assert 0" in str(repr.reprcrash) | ||||
|         s = str(repr) | ||||
|         assert s.startswith('Traceback (most recent call last):\n  File') | ||||
|         assert s.endswith('\nAssertionError: assert 0') | ||||
|         assert 'exec (source.compile())' in s | ||||
|         # python 2.4 fails to get the source line for the assert | ||||
|         if py.std.sys.version_info >= (2, 5): | ||||
|             assert s.count('assert 0') == 2 | ||||
| 
 | ||||
|     def test_traceback_repr_style(self, importasmod): | ||||
|         mod = importasmod(""" | ||||
|             def f(): | ||||
|                 g() | ||||
|             def g(): | ||||
|                 h() | ||||
|             def h(): | ||||
|                 i() | ||||
|             def i(): | ||||
|                 raise ValueError() | ||||
|         """) | ||||
|         excinfo = pytest.raises(ValueError, mod.f) | ||||
|         excinfo.traceback = excinfo.traceback.filter() | ||||
|         excinfo.traceback[1].set_repr_style("short") | ||||
|         excinfo.traceback[2].set_repr_style("short") | ||||
|         r = excinfo.getrepr(style="long") | ||||
|         tw = TWMock() | ||||
|         r.toterminal(tw) | ||||
|         for line in tw.lines: print (line) | ||||
|         assert tw.lines[0] == "" | ||||
|         assert tw.lines[1] == "    def f():" | ||||
|         assert tw.lines[2] == ">       g()" | ||||
|         assert tw.lines[3] == "" | ||||
|         assert tw.lines[4].endswith("mod.py:3: ") | ||||
|         assert tw.lines[5] == ("_ ", None) | ||||
|         assert tw.lines[6].endswith("in g") | ||||
|         assert tw.lines[7] == "    h()" | ||||
|         assert tw.lines[8].endswith("in h") | ||||
|         assert tw.lines[9] == "    i()" | ||||
|         assert tw.lines[10] == ("_ ", None) | ||||
|         assert tw.lines[11] == "" | ||||
|         assert tw.lines[12] == "    def i():" | ||||
|         assert tw.lines[13] == ">       raise ValueError()" | ||||
|         assert tw.lines[14] == "E       ValueError" | ||||
|         assert tw.lines[15] == "" | ||||
|         assert tw.lines[16].endswith("mod.py:9: ValueError") | ||||
|  | @ -0,0 +1,657 @@ | |||
| # flake8: noqa | ||||
| # disable flake check on this file because some constructs are strange | ||||
| # or redundant on purpose and can't be disable on a line-by-line basis | ||||
| import sys | ||||
| 
 | ||||
| import _pytest._code | ||||
| import py | ||||
| import pytest | ||||
| from _pytest._code import Source | ||||
| from _pytest._code.source import _ast | ||||
| 
 | ||||
| if _ast is not None: | ||||
|     astonly = pytest.mark.nothing | ||||
| else: | ||||
|     astonly = pytest.mark.xfail("True", reason="only works with AST-compile") | ||||
| 
 | ||||
| failsonjython = pytest.mark.xfail("sys.platform.startswith('java')") | ||||
| 
 | ||||
| def test_source_str_function(): | ||||
|     x = Source("3") | ||||
|     assert str(x) == "3" | ||||
| 
 | ||||
|     x = Source("   3") | ||||
|     assert str(x) == "3" | ||||
| 
 | ||||
|     x = Source(""" | ||||
|         3 | ||||
|     """, rstrip=False) | ||||
|     assert str(x) == "\n3\n    " | ||||
| 
 | ||||
|     x = Source(""" | ||||
|         3 | ||||
|     """, rstrip=True) | ||||
|     assert str(x) == "\n3" | ||||
| 
 | ||||
| def test_unicode(): | ||||
|     try: | ||||
|         unicode | ||||
|     except NameError: | ||||
|         return | ||||
|     x = Source(unicode("4")) | ||||
|     assert str(x) == "4" | ||||
|     co = _pytest._code.compile(unicode('u"\xc3\xa5"', 'utf8'), mode='eval') | ||||
|     val = eval(co) | ||||
|     assert isinstance(val, unicode) | ||||
| 
 | ||||
| def test_source_from_function(): | ||||
|     source = _pytest._code.Source(test_source_str_function) | ||||
|     assert str(source).startswith('def test_source_str_function():') | ||||
| 
 | ||||
| def test_source_from_method(): | ||||
|     class TestClass: | ||||
|         def test_method(self): | ||||
|             pass | ||||
|     source = _pytest._code.Source(TestClass().test_method) | ||||
|     assert source.lines == ["def test_method(self):", | ||||
|                             "    pass"] | ||||
| 
 | ||||
| def test_source_from_lines(): | ||||
|     lines = ["a \n", "b\n", "c"] | ||||
|     source = _pytest._code.Source(lines) | ||||
|     assert source.lines == ['a ', 'b', 'c'] | ||||
| 
 | ||||
| def test_source_from_inner_function(): | ||||
|     def f(): | ||||
|         pass | ||||
|     source = _pytest._code.Source(f, deindent=False) | ||||
|     assert str(source).startswith('    def f():') | ||||
|     source = _pytest._code.Source(f) | ||||
|     assert str(source).startswith('def f():') | ||||
| 
 | ||||
| def test_source_putaround_simple(): | ||||
|     source = Source("raise ValueError") | ||||
|     source = source.putaround( | ||||
|         "try:", """\ | ||||
|         except ValueError: | ||||
|             x = 42 | ||||
|         else: | ||||
|             x = 23""") | ||||
|     assert str(source)=="""\ | ||||
| try: | ||||
|     raise ValueError | ||||
| except ValueError: | ||||
|     x = 42 | ||||
| else: | ||||
|     x = 23""" | ||||
| 
 | ||||
| def test_source_putaround(): | ||||
|     source = Source() | ||||
|     source = source.putaround(""" | ||||
|         if 1: | ||||
|             x=1 | ||||
|     """) | ||||
|     assert str(source).strip() == "if 1:\n    x=1" | ||||
| 
 | ||||
| def test_source_strips(): | ||||
|     source = Source("") | ||||
|     assert source == Source() | ||||
|     assert str(source) == '' | ||||
|     assert source.strip() == source | ||||
| 
 | ||||
| def test_source_strip_multiline(): | ||||
|     source = Source() | ||||
|     source.lines = ["", " hello", "  "] | ||||
|     source2 = source.strip() | ||||
|     assert source2.lines == [" hello"] | ||||
| 
 | ||||
| def test_syntaxerror_rerepresentation(): | ||||
|     ex = pytest.raises(SyntaxError, _pytest._code.compile, 'xyz xyz') | ||||
|     assert ex.value.lineno == 1 | ||||
|     assert ex.value.offset in (4,7) # XXX pypy/jython versus cpython? | ||||
|     assert ex.value.text.strip(), 'x x' | ||||
| 
 | ||||
| def test_isparseable(): | ||||
|     assert Source("hello").isparseable() | ||||
|     assert Source("if 1:\n  pass").isparseable() | ||||
|     assert Source(" \nif 1:\n  pass").isparseable() | ||||
|     assert not Source("if 1:\n").isparseable() | ||||
|     assert not Source(" \nif 1:\npass").isparseable() | ||||
|     assert not Source(chr(0)).isparseable() | ||||
| 
 | ||||
| class TestAccesses: | ||||
|     source = Source("""\ | ||||
|         def f(x): | ||||
|             pass | ||||
|         def g(x): | ||||
|             pass | ||||
|     """) | ||||
|     def test_getrange(self): | ||||
|         x = self.source[0:2] | ||||
|         assert x.isparseable() | ||||
|         assert len(x.lines) == 2 | ||||
|         assert str(x) == "def f(x):\n    pass" | ||||
| 
 | ||||
|     def test_getline(self): | ||||
|         x = self.source[0] | ||||
|         assert x == "def f(x):" | ||||
| 
 | ||||
|     def test_len(self): | ||||
|         assert len(self.source) == 4 | ||||
| 
 | ||||
|     def test_iter(self): | ||||
|         l = [x for x in self.source] | ||||
|         assert len(l) == 4 | ||||
| 
 | ||||
| class TestSourceParsingAndCompiling: | ||||
|     source = Source("""\ | ||||
|         def f(x): | ||||
|             assert (x == | ||||
|                     3 + | ||||
|                     4) | ||||
|     """).strip() | ||||
| 
 | ||||
|     def test_compile(self): | ||||
|         co = _pytest._code.compile("x=3") | ||||
|         d = {} | ||||
|         exec (co, d) | ||||
|         assert d['x'] == 3 | ||||
| 
 | ||||
|     def test_compile_and_getsource_simple(self): | ||||
|         co = _pytest._code.compile("x=3") | ||||
|         exec (co) | ||||
|         source = _pytest._code.Source(co) | ||||
|         assert str(source) == "x=3" | ||||
| 
 | ||||
|     def test_compile_and_getsource_through_same_function(self): | ||||
|         def gensource(source): | ||||
|             return _pytest._code.compile(source) | ||||
|         co1 = gensource(""" | ||||
|             def f(): | ||||
|                 raise KeyError() | ||||
|         """) | ||||
|         co2 = gensource(""" | ||||
|             def f(): | ||||
|                 raise ValueError() | ||||
|         """) | ||||
|         source1 = py.std.inspect.getsource(co1) | ||||
|         assert 'KeyError' in source1 | ||||
|         source2 = py.std.inspect.getsource(co2) | ||||
|         assert 'ValueError' in source2 | ||||
| 
 | ||||
|     def test_getstatement(self): | ||||
|         #print str(self.source) | ||||
|         ass = str(self.source[1:]) | ||||
|         for i in range(1, 4): | ||||
|             #print "trying start in line %r" % self.source[i] | ||||
|             s = self.source.getstatement(i) | ||||
|             #x = s.deindent() | ||||
|             assert str(s) == ass | ||||
| 
 | ||||
|     def test_getstatementrange_triple_quoted(self): | ||||
|         #print str(self.source) | ||||
|         source = Source("""hello(''' | ||||
|         ''')""") | ||||
|         s = source.getstatement(0) | ||||
|         assert s == str(source) | ||||
|         s = source.getstatement(1) | ||||
|         assert s == str(source) | ||||
| 
 | ||||
|     @astonly | ||||
|     def test_getstatementrange_within_constructs(self): | ||||
|         source = Source("""\ | ||||
|             try: | ||||
|                 try: | ||||
|                     raise ValueError | ||||
|                 except SomeThing: | ||||
|                     pass | ||||
|             finally: | ||||
|                 42 | ||||
|         """) | ||||
|         assert len(source) == 7 | ||||
|         # check all lineno's that could occur in a traceback | ||||
|         #assert source.getstatementrange(0) == (0, 7) | ||||
|         #assert source.getstatementrange(1) == (1, 5) | ||||
|         assert source.getstatementrange(2) == (2, 3) | ||||
|         assert source.getstatementrange(3) == (3, 4) | ||||
|         assert source.getstatementrange(4) == (4, 5) | ||||
|         #assert source.getstatementrange(5) == (0, 7) | ||||
|         assert source.getstatementrange(6) == (6, 7) | ||||
| 
 | ||||
|     def test_getstatementrange_bug(self): | ||||
|         source = Source("""\ | ||||
|             try: | ||||
|                 x = ( | ||||
|                    y + | ||||
|                    z) | ||||
|             except: | ||||
|                 pass | ||||
|         """) | ||||
|         assert len(source) == 6 | ||||
|         assert source.getstatementrange(2) == (1, 4) | ||||
| 
 | ||||
|     def test_getstatementrange_bug2(self): | ||||
|         source = Source("""\ | ||||
|             assert ( | ||||
|                 33 | ||||
|                 == | ||||
|                 [ | ||||
|                   X(3, | ||||
|                       b=1, c=2 | ||||
|                    ), | ||||
|                 ] | ||||
|               ) | ||||
|         """) | ||||
|         assert len(source) == 9 | ||||
|         assert source.getstatementrange(5) == (0, 9) | ||||
| 
 | ||||
|     def test_getstatementrange_ast_issue58(self): | ||||
|         source = Source("""\ | ||||
| 
 | ||||
|             def test_some(): | ||||
|                 for a in [a for a in | ||||
|                     CAUSE_ERROR]: pass | ||||
| 
 | ||||
|             x = 3 | ||||
|         """) | ||||
|         assert getstatement(2, source).lines == source.lines[2:3] | ||||
|         assert getstatement(3, source).lines == source.lines[3:4] | ||||
| 
 | ||||
|     @pytest.mark.skipif("sys.version_info < (2,6)") | ||||
|     def test_getstatementrange_out_of_bounds_py3(self): | ||||
|         source = Source("if xxx:\n   from .collections import something") | ||||
|         r = source.getstatementrange(1) | ||||
|         assert r == (1,2) | ||||
| 
 | ||||
|     def test_getstatementrange_with_syntaxerror_issue7(self): | ||||
|         source = Source(":") | ||||
|         pytest.raises(SyntaxError, lambda: source.getstatementrange(0)) | ||||
| 
 | ||||
|     @pytest.mark.skipif("sys.version_info < (2,6)") | ||||
|     def test_compile_to_ast(self): | ||||
|         import ast | ||||
|         source = Source("x = 4") | ||||
|         mod = source.compile(flag=ast.PyCF_ONLY_AST) | ||||
|         assert isinstance(mod, ast.Module) | ||||
|         compile(mod, "<filename>", "exec") | ||||
| 
 | ||||
|     def test_compile_and_getsource(self): | ||||
|         co = self.source.compile() | ||||
|         py.builtin.exec_(co, globals()) | ||||
|         f(7) | ||||
|         excinfo = pytest.raises(AssertionError, "f(6)") | ||||
|         frame = excinfo.traceback[-1].frame | ||||
|         stmt = frame.code.fullsource.getstatement(frame.lineno) | ||||
|         #print "block", str(block) | ||||
|         assert str(stmt).strip().startswith('assert') | ||||
| 
 | ||||
|     def test_compilefuncs_and_path_sanity(self): | ||||
|         def check(comp, name): | ||||
|             co = comp(self.source, name) | ||||
|             if not name: | ||||
|                 expected = "codegen %s:%d>" %(mypath, mylineno+2+1) | ||||
|             else: | ||||
|                 expected = "codegen %r %s:%d>" % (name, mypath, mylineno+2+1) | ||||
|             fn = co.co_filename | ||||
|             assert fn.endswith(expected) | ||||
| 
 | ||||
|         mycode = _pytest._code.Code(self.test_compilefuncs_and_path_sanity) | ||||
|         mylineno = mycode.firstlineno | ||||
|         mypath = mycode.path | ||||
| 
 | ||||
|         for comp in _pytest._code.compile, _pytest._code.Source.compile: | ||||
|             for name in '', None, 'my': | ||||
|                 yield check, comp, name | ||||
| 
 | ||||
|     def test_offsetless_synerr(self): | ||||
|         pytest.raises(SyntaxError, _pytest._code.compile, "lambda a,a: 0", mode='eval') | ||||
| 
 | ||||
| def test_getstartingblock_singleline(): | ||||
|     class A: | ||||
|         def __init__(self, *args): | ||||
|             frame = sys._getframe(1) | ||||
|             self.source = _pytest._code.Frame(frame).statement | ||||
| 
 | ||||
|     x = A('x', 'y') | ||||
| 
 | ||||
|     l = [i for i in x.source.lines if i.strip()] | ||||
|     assert len(l) == 1 | ||||
| 
 | ||||
| def test_getstartingblock_multiline(): | ||||
|     class A: | ||||
|         def __init__(self, *args): | ||||
|             frame = sys._getframe(1) | ||||
|             self.source = _pytest._code.Frame(frame).statement | ||||
| 
 | ||||
|     x = A('x', | ||||
|           'y' \ | ||||
|           , | ||||
|           'z') | ||||
| 
 | ||||
|     l = [i for i in x.source.lines if i.strip()] | ||||
|     assert len(l) == 4 | ||||
| 
 | ||||
| def test_getline_finally(): | ||||
|     def c(): pass | ||||
|     excinfo = pytest.raises(TypeError, """ | ||||
|            teardown = None | ||||
|            try: | ||||
|                 c(1) | ||||
|            finally: | ||||
|                 if teardown: | ||||
|                     teardown() | ||||
|     """) | ||||
|     source = excinfo.traceback[-1].statement | ||||
|     assert str(source).strip() == 'c(1)' | ||||
| 
 | ||||
| def test_getfuncsource_dynamic(): | ||||
|     source = """ | ||||
|         def f(): | ||||
|             raise ValueError | ||||
| 
 | ||||
|         def g(): pass | ||||
|     """ | ||||
|     co = _pytest._code.compile(source) | ||||
|     py.builtin.exec_(co, globals()) | ||||
|     assert str(_pytest._code.Source(f)).strip() == 'def f():\n    raise ValueError' | ||||
|     assert str(_pytest._code.Source(g)).strip() == 'def g(): pass' | ||||
| 
 | ||||
| 
 | ||||
| def test_getfuncsource_with_multine_string(): | ||||
|     def f(): | ||||
|         c = '''while True: | ||||
|     pass | ||||
| ''' | ||||
|     assert str(_pytest._code.Source(f)).strip() == "def f():\n    c = '''while True:\n    pass\n'''" | ||||
| 
 | ||||
| 
 | ||||
| def test_deindent(): | ||||
|     from _pytest._code.source import deindent as deindent | ||||
|     assert deindent(['\tfoo', '\tbar', ]) == ['foo', 'bar'] | ||||
| 
 | ||||
|     def f(): | ||||
|         c = '''while True: | ||||
|     pass | ||||
| ''' | ||||
|     import inspect | ||||
|     lines = deindent(inspect.getsource(f).splitlines()) | ||||
|     assert lines == ["def f():", "    c = '''while True:", "    pass", "'''"] | ||||
| 
 | ||||
|     source = """ | ||||
|         def f(): | ||||
|             def g(): | ||||
|                 pass | ||||
|     """ | ||||
|     lines = deindent(source.splitlines()) | ||||
|     assert lines == ['', 'def f():', '    def g():', '        pass', '    '] | ||||
| 
 | ||||
| @pytest.mark.xfail("sys.version_info[:3] < (2,7,0) or " | ||||
|     "((3,0) <= sys.version_info[:2] < (3,2))") | ||||
| def test_source_of_class_at_eof_without_newline(tmpdir): | ||||
|     # this test fails because the implicit inspect.getsource(A) below | ||||
|     # does not return the "x = 1" last line. | ||||
|     source = _pytest._code.Source(''' | ||||
|         class A(object): | ||||
|             def method(self): | ||||
|                 x = 1 | ||||
|     ''') | ||||
|     path = tmpdir.join("a.py") | ||||
|     path.write(source) | ||||
|     s2 = _pytest._code.Source(tmpdir.join("a.py").pyimport().A) | ||||
|     assert str(source).strip() == str(s2).strip() | ||||
| 
 | ||||
| if True: | ||||
|     def x(): | ||||
|         pass | ||||
| 
 | ||||
| def test_getsource_fallback(): | ||||
|     from _pytest._code.source import getsource | ||||
|     expected = """def x(): | ||||
|     pass""" | ||||
|     src = getsource(x) | ||||
|     assert src == expected | ||||
| 
 | ||||
| def test_idem_compile_and_getsource(): | ||||
|     from _pytest._code.source import getsource | ||||
|     expected = "def x(): pass" | ||||
|     co = _pytest._code.compile(expected) | ||||
|     src = getsource(co) | ||||
|     assert src == expected | ||||
| 
 | ||||
| def test_findsource_fallback(): | ||||
|     from _pytest._code.source import findsource | ||||
|     src, lineno = findsource(x) | ||||
|     assert 'test_findsource_simple' in str(src) | ||||
|     assert src[lineno] == '    def x():' | ||||
| 
 | ||||
| def test_findsource(): | ||||
|     from _pytest._code.source import findsource | ||||
|     co = _pytest._code.compile("""if 1: | ||||
|     def x(): | ||||
|         pass | ||||
| """) | ||||
| 
 | ||||
|     src, lineno = findsource(co) | ||||
|     assert 'if 1:' in str(src) | ||||
| 
 | ||||
|     d = {} | ||||
|     eval(co, d) | ||||
|     src, lineno = findsource(d['x']) | ||||
|     assert 'if 1:' in str(src) | ||||
|     assert src[lineno] == "    def x():" | ||||
| 
 | ||||
| 
 | ||||
| def test_getfslineno(): | ||||
|     from _pytest._code import getfslineno | ||||
| 
 | ||||
|     def f(x): | ||||
|         pass | ||||
| 
 | ||||
|     fspath, lineno = getfslineno(f) | ||||
| 
 | ||||
|     assert fspath.basename == "test_source.py" | ||||
|     assert lineno == _pytest._code.getrawcode(f).co_firstlineno - 1 # see findsource | ||||
| 
 | ||||
|     class A(object): | ||||
|         pass | ||||
| 
 | ||||
|     fspath, lineno = getfslineno(A) | ||||
| 
 | ||||
|     _, A_lineno = py.std.inspect.findsource(A) | ||||
|     assert fspath.basename == "test_source.py" | ||||
|     assert lineno == A_lineno | ||||
| 
 | ||||
|     assert getfslineno(3) == ("", -1) | ||||
|     class B: | ||||
|         pass | ||||
|     B.__name__ = "B2" | ||||
|     assert getfslineno(B)[1] == -1 | ||||
| 
 | ||||
| def test_code_of_object_instance_with_call(): | ||||
|     class A: | ||||
|         pass | ||||
|     pytest.raises(TypeError, lambda: _pytest._code.Source(A())) | ||||
|     class WithCall: | ||||
|         def __call__(self): | ||||
|             pass | ||||
| 
 | ||||
|     code = _pytest._code.Code(WithCall()) | ||||
|     assert 'pass' in str(code.source()) | ||||
| 
 | ||||
|     class Hello(object): | ||||
|         def __call__(self): | ||||
|             pass | ||||
|     pytest.raises(TypeError, lambda: _pytest._code.Code(Hello)) | ||||
| 
 | ||||
| 
 | ||||
| def getstatement(lineno, source): | ||||
|     from _pytest._code.source import getstatementrange_ast | ||||
|     source = _pytest._code.Source(source, deindent=False) | ||||
|     ast, start, end = getstatementrange_ast(lineno, source) | ||||
|     return source[start:end] | ||||
| 
 | ||||
| def test_oneline(): | ||||
|     source = getstatement(0, "raise ValueError") | ||||
|     assert str(source) == "raise ValueError" | ||||
| 
 | ||||
| def test_comment_and_no_newline_at_end(): | ||||
|     from _pytest._code.source import getstatementrange_ast | ||||
|     source = Source(['def test_basic_complex():', | ||||
|                      '    assert 1 == 2', | ||||
|                      '# vim: filetype=pyopencl:fdm=marker']) | ||||
|     ast, start, end = getstatementrange_ast(1, source) | ||||
|     assert end == 2 | ||||
| 
 | ||||
| def test_oneline_and_comment(): | ||||
|     source = getstatement(0, "raise ValueError\n#hello") | ||||
|     assert str(source) == "raise ValueError" | ||||
| 
 | ||||
| def test_comments(): | ||||
|     source = '''def test(): | ||||
|     "comment 1" | ||||
|     x = 1 | ||||
|       # comment 2 | ||||
|     # comment 3 | ||||
| 
 | ||||
|     assert False | ||||
| 
 | ||||
| """ | ||||
| comment 4 | ||||
| """ | ||||
| ''' | ||||
|     for line in range(2,6): | ||||
|         assert str(getstatement(line, source)) == '    x = 1' | ||||
|     for line in range(6,10): | ||||
|         assert str(getstatement(line, source)) == '    assert False' | ||||
|     assert str(getstatement(10, source)) == '"""' | ||||
| 
 | ||||
| def test_comment_in_statement(): | ||||
|     source = '''test(foo=1, | ||||
|     # comment 1 | ||||
|     bar=2) | ||||
| ''' | ||||
|     for line in range(1,3): | ||||
|         assert str(getstatement(line, source)) == \ | ||||
|                'test(foo=1,\n    # comment 1\n    bar=2)' | ||||
| 
 | ||||
| def test_single_line_else(): | ||||
|     source = getstatement(1, "if False: 2\nelse: 3") | ||||
|     assert str(source) == "else: 3" | ||||
| 
 | ||||
| def test_single_line_finally(): | ||||
|     source = getstatement(1, "try: 1\nfinally: 3") | ||||
|     assert str(source) == "finally: 3" | ||||
| 
 | ||||
| def test_issue55(): | ||||
|     source = ('def round_trip(dinp):\n  assert 1 == dinp\n' | ||||
|               'def test_rt():\n  round_trip("""\n""")\n') | ||||
|     s = getstatement(3, source) | ||||
|     assert str(s) == '  round_trip("""\n""")' | ||||
| 
 | ||||
| 
 | ||||
| def XXXtest_multiline(): | ||||
|     source = getstatement(0, """\ | ||||
| raise ValueError( | ||||
|     23 | ||||
| ) | ||||
| x = 3 | ||||
| """) | ||||
|     assert str(source) == "raise ValueError(\n    23\n)" | ||||
| 
 | ||||
| class TestTry: | ||||
|     pytestmark = astonly | ||||
|     source = """\ | ||||
| try: | ||||
|     raise ValueError | ||||
| except Something: | ||||
|     raise IndexError(1) | ||||
| else: | ||||
|     raise KeyError() | ||||
| """ | ||||
| 
 | ||||
|     def test_body(self): | ||||
|         source = getstatement(1, self.source) | ||||
|         assert str(source) == "    raise ValueError" | ||||
| 
 | ||||
|     def test_except_line(self): | ||||
|         source = getstatement(2, self.source) | ||||
|         assert str(source) == "except Something:" | ||||
| 
 | ||||
|     def test_except_body(self): | ||||
|         source = getstatement(3, self.source) | ||||
|         assert str(source) == "    raise IndexError(1)" | ||||
| 
 | ||||
|     def test_else(self): | ||||
|         source = getstatement(5, self.source) | ||||
|         assert str(source) == "    raise KeyError()" | ||||
| 
 | ||||
| class TestTryFinally: | ||||
|     source = """\ | ||||
| try: | ||||
|     raise ValueError | ||||
| finally: | ||||
|     raise IndexError(1) | ||||
| """ | ||||
| 
 | ||||
|     def test_body(self): | ||||
|         source = getstatement(1, self.source) | ||||
|         assert str(source) == "    raise ValueError" | ||||
| 
 | ||||
|     def test_finally(self): | ||||
|         source = getstatement(3, self.source) | ||||
|         assert str(source) == "    raise IndexError(1)" | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| class TestIf: | ||||
|     pytestmark = astonly | ||||
|     source = """\ | ||||
| if 1: | ||||
|     y = 3 | ||||
| elif False: | ||||
|     y = 5 | ||||
| else: | ||||
|     y = 7 | ||||
| """ | ||||
| 
 | ||||
|     def test_body(self): | ||||
|         source = getstatement(1, self.source) | ||||
|         assert str(source) == "    y = 3" | ||||
| 
 | ||||
|     def test_elif_clause(self): | ||||
|         source = getstatement(2, self.source) | ||||
|         assert str(source) == "elif False:" | ||||
| 
 | ||||
|     def test_elif(self): | ||||
|         source = getstatement(3, self.source) | ||||
|         assert str(source) == "    y = 5" | ||||
| 
 | ||||
|     def test_else(self): | ||||
|         source = getstatement(5, self.source) | ||||
|         assert str(source) == "    y = 7" | ||||
| 
 | ||||
| def test_semicolon(): | ||||
|     s = """\ | ||||
| hello ; pytest.skip() | ||||
| """ | ||||
|     source = getstatement(0, s) | ||||
|     assert str(source) == s.strip() | ||||
| 
 | ||||
| def test_def_online(): | ||||
|     s = """\ | ||||
| def func(): raise ValueError(42) | ||||
| 
 | ||||
| def something(): | ||||
|     pass | ||||
| """ | ||||
|     source = getstatement(0, s) | ||||
|     assert str(source) == "def func(): raise ValueError(42)" | ||||
| 
 | ||||
| def XXX_test_expression_multiline(): | ||||
|     source = """\ | ||||
| something | ||||
| ''' | ||||
| '''""" | ||||
|     result = getstatement(1, source) | ||||
|     assert str(result) == "'''\n'''" | ||||
| 
 | ||||
|  | @ -1,6 +1,9 @@ | |||
| import sys | ||||
| from textwrap import dedent | ||||
| import pytest, py | ||||
| 
 | ||||
| import _pytest._code | ||||
| import py | ||||
| import pytest | ||||
| from _pytest.main import EXIT_NOTESTSCOLLECTED | ||||
| 
 | ||||
| 
 | ||||
|  | @ -598,13 +601,13 @@ class TestConftestCustomization: | |||
| 
 | ||||
|     def test_customized_pymakemodule_issue205_subdir(self, testdir): | ||||
|         b = testdir.mkdir("a").mkdir("b") | ||||
|         b.join("conftest.py").write(py.code.Source(""" | ||||
|         b.join("conftest.py").write(_pytest._code.Source(""" | ||||
|             def pytest_pycollect_makemodule(__multicall__): | ||||
|                 mod = __multicall__.execute() | ||||
|                 mod.obj.hello = "world" | ||||
|                 return mod | ||||
|         """)) | ||||
|         b.join("test_module.py").write(py.code.Source(""" | ||||
|         b.join("test_module.py").write(_pytest._code.Source(""" | ||||
|             def test_hello(): | ||||
|                 assert hello == "world" | ||||
|         """)) | ||||
|  | @ -613,7 +616,7 @@ class TestConftestCustomization: | |||
| 
 | ||||
|     def test_customized_pymakeitem(self, testdir): | ||||
|         b = testdir.mkdir("a").mkdir("b") | ||||
|         b.join("conftest.py").write(py.code.Source(""" | ||||
|         b.join("conftest.py").write(_pytest._code.Source(""" | ||||
|             import pytest | ||||
|             @pytest.hookimpl(hookwrapper=True) | ||||
|             def pytest_pycollect_makeitem(): | ||||
|  | @ -624,7 +627,7 @@ class TestConftestCustomization: | |||
|                         for func in result: | ||||
|                             func._some123 = "world" | ||||
|         """)) | ||||
|         b.join("test_module.py").write(py.code.Source(""" | ||||
|         b.join("test_module.py").write(_pytest._code.Source(""" | ||||
|             import pytest | ||||
| 
 | ||||
|             @pytest.fixture() | ||||
|  | @ -662,7 +665,7 @@ class TestConftestCustomization: | |||
| def test_setup_only_available_in_subdir(testdir): | ||||
|     sub1 = testdir.mkpydir("sub1") | ||||
|     sub2 = testdir.mkpydir("sub2") | ||||
|     sub1.join("conftest.py").write(py.code.Source(""" | ||||
|     sub1.join("conftest.py").write(_pytest._code.Source(""" | ||||
|         import pytest | ||||
|         def pytest_runtest_setup(item): | ||||
|             assert item.fspath.purebasename == "test_in_sub1" | ||||
|  | @ -671,7 +674,7 @@ def test_setup_only_available_in_subdir(testdir): | |||
|         def pytest_runtest_teardown(item): | ||||
|             assert item.fspath.purebasename == "test_in_sub1" | ||||
|     """)) | ||||
|     sub2.join("conftest.py").write(py.code.Source(""" | ||||
|     sub2.join("conftest.py").write(_pytest._code.Source(""" | ||||
|         import pytest | ||||
|         def pytest_runtest_setup(item): | ||||
|             assert item.fspath.purebasename == "test_in_sub2" | ||||
|  | @ -787,7 +790,7 @@ class TestTracebackCutting: | |||
|         except ValueError: | ||||
|             _, _, tb = sys.exc_info() | ||||
| 
 | ||||
|         tb = py.code.Traceback(tb) | ||||
|         tb = _pytest._code.Traceback(tb) | ||||
|         assert isinstance(tb[-1].path, str) | ||||
|         assert not filter_traceback(tb[-1]) | ||||
| 
 | ||||
|  | @ -810,7 +813,7 @@ class TestTracebackCutting: | |||
|             _, _, tb = sys.exc_info() | ||||
| 
 | ||||
|         testdir.tmpdir.join('filter_traceback_entry_as_str.py').remove() | ||||
|         tb = py.code.Traceback(tb) | ||||
|         tb = _pytest._code.Traceback(tb) | ||||
|         assert isinstance(tb[-1].path, str) | ||||
|         assert filter_traceback(tb[-1]) | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,9 +1,13 @@ | |||
| import pytest, py, sys | ||||
| from _pytest import python as funcargs | ||||
| from _pytest.python import FixtureLookupError | ||||
| from _pytest.pytester import get_public_names | ||||
| from textwrap import dedent | ||||
| 
 | ||||
| import _pytest._code | ||||
| import pytest | ||||
| import sys | ||||
| from _pytest import python as funcargs | ||||
| from _pytest.pytester import get_public_names | ||||
| from _pytest.python import FixtureLookupError | ||||
| 
 | ||||
| 
 | ||||
| def test_getfuncargnames(): | ||||
|     def f(): pass | ||||
|     assert not funcargs.getfuncargnames(f) | ||||
|  | @ -86,12 +90,12 @@ class TestFillFixtures: | |||
|     def test_conftest_funcargs_only_available_in_subdir(self, testdir): | ||||
|         sub1 = testdir.mkpydir("sub1") | ||||
|         sub2 = testdir.mkpydir("sub2") | ||||
|         sub1.join("conftest.py").write(py.code.Source(""" | ||||
|         sub1.join("conftest.py").write(_pytest._code.Source(""" | ||||
|             import pytest | ||||
|             def pytest_funcarg__arg1(request): | ||||
|                 pytest.raises(Exception, "request.getfuncargvalue('arg2')") | ||||
|         """)) | ||||
|         sub2.join("conftest.py").write(py.code.Source(""" | ||||
|         sub2.join("conftest.py").write(_pytest._code.Source(""" | ||||
|             import pytest | ||||
|             def pytest_funcarg__arg2(request): | ||||
|                 pytest.raises(Exception, "request.getfuncargvalue('arg1')") | ||||
|  | @ -156,7 +160,7 @@ class TestFillFixtures: | |||
|                 return 'spam' | ||||
|         """) | ||||
|         pkg = testdir.mkpydir("pkg") | ||||
|         pkg.join("conftest.py").write(py.code.Source(""" | ||||
|         pkg.join("conftest.py").write(_pytest._code.Source(""" | ||||
|             import pytest | ||||
| 
 | ||||
|             @pytest.fixture | ||||
|  | @ -164,7 +168,7 @@ class TestFillFixtures: | |||
|                 return spam * 2 | ||||
|         """)) | ||||
|         testfile = pkg.join("test_spam.py") | ||||
|         testfile.write(py.code.Source(""" | ||||
|         testfile.write(_pytest._code.Source(""" | ||||
|             def test_spam(spam): | ||||
|                 assert spam == "spamspam" | ||||
|         """)) | ||||
|  | @ -258,7 +262,7 @@ class TestFillFixtures: | |||
|                 return request.param | ||||
|         """) | ||||
|         subdir = testdir.mkpydir('subdir') | ||||
|         subdir.join("conftest.py").write(py.code.Source(""" | ||||
|         subdir.join("conftest.py").write(_pytest._code.Source(""" | ||||
|             import pytest | ||||
| 
 | ||||
|             @pytest.fixture | ||||
|  | @ -266,7 +270,7 @@ class TestFillFixtures: | |||
|                 return 'spam' | ||||
|         """)) | ||||
|         testfile = subdir.join("test_spam.py") | ||||
|         testfile.write(py.code.Source(""" | ||||
|         testfile.write(_pytest._code.Source(""" | ||||
|             def test_spam(spam): | ||||
|                 assert spam == "spam" | ||||
|         """)) | ||||
|  | @ -312,7 +316,7 @@ class TestFillFixtures: | |||
|                 return 'spam' | ||||
|         """) | ||||
|         subdir = testdir.mkpydir('subdir') | ||||
|         subdir.join("conftest.py").write(py.code.Source(""" | ||||
|         subdir.join("conftest.py").write(_pytest._code.Source(""" | ||||
|             import pytest | ||||
| 
 | ||||
|             @pytest.fixture(params=[1, 2, 3]) | ||||
|  | @ -320,7 +324,7 @@ class TestFillFixtures: | |||
|                 return request.param | ||||
|         """)) | ||||
|         testfile = subdir.join("test_spam.py") | ||||
|         testfile.write(py.code.Source(""" | ||||
|         testfile.write(_pytest._code.Source(""" | ||||
|             params = {'spam': 1} | ||||
| 
 | ||||
|             def test_spam(spam): | ||||
|  | @ -609,7 +613,7 @@ class TestRequestBasic: | |||
|     def test_fixtures_sub_subdir_normalize_sep(self, testdir): | ||||
|         # this tests that normalization of nodeids takes place | ||||
|         b = testdir.mkdir("tests").mkdir("unit") | ||||
|         b.join("conftest.py").write(py.code.Source(""" | ||||
|         b.join("conftest.py").write(_pytest._code.Source(""" | ||||
|             def pytest_funcarg__arg1(): | ||||
|                 pass | ||||
|         """)) | ||||
|  | @ -1349,7 +1353,7 @@ class TestAutouseDiscovery: | |||
| class TestAutouseManagement: | ||||
|     def test_autouse_conftest_mid_directory(self, testdir): | ||||
|         pkgdir = testdir.mkpydir("xyz123") | ||||
|         pkgdir.join("conftest.py").write(py.code.Source(""" | ||||
|         pkgdir.join("conftest.py").write(_pytest._code.Source(""" | ||||
|             import pytest | ||||
|             @pytest.fixture(autouse=True) | ||||
|             def app(): | ||||
|  | @ -1357,7 +1361,7 @@ class TestAutouseManagement: | |||
|                 sys._myapp = "hello" | ||||
|         """)) | ||||
|         t = pkgdir.ensure("tests", "test_app.py") | ||||
|         t.write(py.code.Source(""" | ||||
|         t.write(_pytest._code.Source(""" | ||||
|             import sys | ||||
|             def test_app(): | ||||
|                 assert sys._myapp == "hello" | ||||
|  |  | |||
|  | @ -1,6 +1,7 @@ | |||
| import pytest | ||||
| from _pytest import runner | ||||
| from _pytest import python | ||||
| from _pytest import runner | ||||
| 
 | ||||
| 
 | ||||
| class TestOEJSKITSpecials: | ||||
|     def test_funcarg_non_pycollectobj(self, testdir): # rough jstests usage | ||||
|  |  | |||
|  | @ -1,7 +1,9 @@ | |||
| # -*- coding: utf-8 -*- | ||||
| import re | ||||
| 
 | ||||
| import pytest, py | ||||
| import _pytest._code | ||||
| import py | ||||
| import pytest | ||||
| from _pytest import python as funcargs | ||||
| 
 | ||||
| class TestMetafunc: | ||||
|  | @ -838,11 +840,11 @@ class TestMetafuncFunctional: | |||
|     def test_generate_tests_only_done_in_subdir(self, testdir): | ||||
|         sub1 = testdir.mkpydir("sub1") | ||||
|         sub2 = testdir.mkpydir("sub2") | ||||
|         sub1.join("conftest.py").write(py.code.Source(""" | ||||
|         sub1.join("conftest.py").write(_pytest._code.Source(""" | ||||
|             def pytest_generate_tests(metafunc): | ||||
|                 assert metafunc.function.__name__ == "test_1" | ||||
|         """)) | ||||
|         sub2.join("conftest.py").write(py.code.Source(""" | ||||
|         sub2.join("conftest.py").write(_pytest._code.Source(""" | ||||
|             def pytest_generate_tests(metafunc): | ||||
|                 assert metafunc.function.__name__ == "test_2" | ||||
|         """)) | ||||
|  |  | |||
|  | @ -38,10 +38,11 @@ class TestRaises: | |||
|         testdir.makepyfile(""" | ||||
|             from __future__ import with_statement | ||||
|             import py, pytest | ||||
|             import _pytest._code | ||||
| 
 | ||||
|             def test_simple(): | ||||
|                 with pytest.raises(ZeroDivisionError) as excinfo: | ||||
|                     assert isinstance(excinfo, py.code.ExceptionInfo) | ||||
|                     assert isinstance(excinfo, _pytest._code.ExceptionInfo) | ||||
|                     1/0 | ||||
|                 print (excinfo) | ||||
|                 assert excinfo.type == ZeroDivisionError | ||||
|  |  | |||
|  | @ -1,8 +1,9 @@ | |||
| "PYTEST_DONT_REWRITE" | ||||
| import pytest, py | ||||
| 
 | ||||
| import py | ||||
| import pytest | ||||
| from _pytest.assertion import util | ||||
| 
 | ||||
| 
 | ||||
| def exvalue(): | ||||
|     return py.std.sys.exc_info()[1] | ||||
| 
 | ||||
|  |  | |||
|  | @ -2,8 +2,10 @@ | |||
| import sys | ||||
| import textwrap | ||||
| 
 | ||||
| import py, pytest | ||||
| import _pytest.assertion as plugin | ||||
| import _pytest._code | ||||
| import py | ||||
| import pytest | ||||
| from _pytest.assertion import reinterpret | ||||
| from _pytest.assertion import util | ||||
| 
 | ||||
|  | @ -22,7 +24,7 @@ def mock_config(): | |||
| 
 | ||||
| 
 | ||||
| def interpret(expr): | ||||
|     return reinterpret.reinterpret(expr, py.code.Frame(sys._getframe(1))) | ||||
|     return reinterpret.reinterpret(expr, _pytest._code.Frame(sys._getframe(1))) | ||||
| 
 | ||||
| class TestBinReprIntegration: | ||||
| 
 | ||||
|  |  | |||
|  | @ -10,6 +10,7 @@ if sys.platform.startswith("java"): | |||
|     # XXX should be xfail | ||||
|     pytest.skip("assert rewrite does currently not work on jython") | ||||
| 
 | ||||
| import _pytest._code | ||||
| from _pytest.assertion import util | ||||
| from _pytest.assertion.rewrite import rewrite_asserts, PYTEST_TAG | ||||
| from _pytest.main import EXIT_NOTESTSCOLLECTED | ||||
|  | @ -17,7 +18,7 @@ from _pytest.main import EXIT_NOTESTSCOLLECTED | |||
| 
 | ||||
| def setup_module(mod): | ||||
|     mod._old_reprcompare = util._reprcompare | ||||
|     py.code._reprcompare = None | ||||
|     _pytest._code._reprcompare = None | ||||
| 
 | ||||
| def teardown_module(mod): | ||||
|     util._reprcompare = mod._old_reprcompare | ||||
|  | @ -31,7 +32,7 @@ def rewrite(src): | |||
| 
 | ||||
| def getmsg(f, extra_ns=None, must_pass=False): | ||||
|     """Rewrite the assertions in f, run it, and get the failure message.""" | ||||
|     src = '\n'.join(py.code.Code(f).source().lines) | ||||
|     src = '\n'.join(_pytest._code.Code(f).source().lines) | ||||
|     mod = rewrite(src) | ||||
|     code = compile(mod, "<test>", "exec") | ||||
|     ns = {} | ||||
|  | @ -669,7 +670,7 @@ class TestAssertionRewriteHookDetails(object): | |||
|         """Implement optional PEP302 api (#808). | ||||
|         """ | ||||
|         path = testdir.mkpydir("foo") | ||||
|         path.join("test_foo.py").write(py.code.Source(""" | ||||
|         path.join("test_foo.py").write(_pytest._code.Source(""" | ||||
|             class Test: | ||||
|                 def test_foo(self): | ||||
|                     import pkgutil | ||||
|  |  | |||
|  | @ -1,8 +1,9 @@ | |||
| import sys | ||||
| 
 | ||||
| import _pytest | ||||
| import pytest | ||||
| import os | ||||
| import shutil | ||||
| import py | ||||
| 
 | ||||
| pytest_plugins = "pytester", | ||||
| 
 | ||||
|  | @ -129,6 +130,7 @@ def test_cache_show(testdir): | |||
| 
 | ||||
| 
 | ||||
| class TestLastFailed: | ||||
| 
 | ||||
|     def test_lastfailed_usecase(self, testdir, monkeypatch): | ||||
|         monkeypatch.setenv("PYTHONDONTWRITEBYTECODE", 1) | ||||
|         p = testdir.makepyfile(""" | ||||
|  | @ -143,7 +145,7 @@ class TestLastFailed: | |||
|         result.stdout.fnmatch_lines([ | ||||
|             "*2 failed*", | ||||
|         ]) | ||||
|         p.write(py.code.Source(""" | ||||
|         p.write(_pytest._code.Source(""" | ||||
|             def test_1(): | ||||
|                 assert 1 | ||||
| 
 | ||||
|  | @ -175,11 +177,11 @@ class TestLastFailed: | |||
|         ]) | ||||
| 
 | ||||
|     def test_failedfirst_order(self, testdir): | ||||
|         testdir.tmpdir.join('test_a.py').write(py.code.Source(""" | ||||
|         testdir.tmpdir.join('test_a.py').write(_pytest._code.Source(""" | ||||
|             def test_always_passes(): | ||||
|                 assert 1 | ||||
|         """)) | ||||
|         testdir.tmpdir.join('test_b.py').write(py.code.Source(""" | ||||
|         testdir.tmpdir.join('test_b.py').write(_pytest._code.Source(""" | ||||
|             def test_always_fails(): | ||||
|                 assert 0 | ||||
|         """)) | ||||
|  | @ -218,7 +220,7 @@ class TestLastFailed: | |||
|         result.stdout.fnmatch_lines([ | ||||
|             "*1 failed*", | ||||
|         ]) | ||||
|         p2.write(py.code.Source(""" | ||||
|         p2.write(_pytest._code.Source(""" | ||||
|             def test_b1(): | ||||
|                 assert 1 | ||||
|         """)) | ||||
|  | @ -238,7 +240,7 @@ class TestLastFailed: | |||
|                 assert 0 | ||||
|         """) | ||||
|         p2 = testdir.tmpdir.join("test_something.py") | ||||
|         p2.write(py.code.Source(""" | ||||
|         p2.write(_pytest._code.Source(""" | ||||
|             def test_2(): | ||||
|                 assert 0 | ||||
|         """)) | ||||
|  |  | |||
|  | @ -4,6 +4,8 @@ from __future__ import with_statement | |||
| import pickle | ||||
| import os | ||||
| import sys | ||||
| 
 | ||||
| import _pytest._code | ||||
| import py | ||||
| import pytest | ||||
| import contextlib | ||||
|  | @ -481,7 +483,7 @@ class TestCaptureFixture: | |||
| 
 | ||||
| def test_setup_failure_does_not_kill_capturing(testdir): | ||||
|     sub1 = testdir.mkpydir("sub1") | ||||
|     sub1.join("conftest.py").write(py.code.Source(""" | ||||
|     sub1.join("conftest.py").write(_pytest._code.Source(""" | ||||
|         def pytest_runtest_setup(item): | ||||
|             raise ValueError(42) | ||||
|     """)) | ||||
|  |  | |||
|  | @ -1,5 +1,6 @@ | |||
| import py, pytest | ||||
| 
 | ||||
| import _pytest._code | ||||
| from _pytest.config import getcfg, get_common_ancestor, determine_setup | ||||
| from _pytest.main import EXIT_NOTESTSCOLLECTED | ||||
| 
 | ||||
|  | @ -7,7 +8,7 @@ class TestParseIni: | |||
|     def test_getcfg_and_config(self, testdir, tmpdir): | ||||
|         sub = tmpdir.mkdir("sub") | ||||
|         sub.chdir() | ||||
|         tmpdir.join("setup.cfg").write(py.code.Source(""" | ||||
|         tmpdir.join("setup.cfg").write(_pytest._code.Source(""" | ||||
|             [pytest] | ||||
|             name = value | ||||
|         """)) | ||||
|  | @ -21,7 +22,7 @@ class TestParseIni: | |||
| 
 | ||||
|     def test_append_parse_args(self, testdir, tmpdir, monkeypatch): | ||||
|         monkeypatch.setenv('PYTEST_ADDOPTS', '--color no -rs --tb="short"') | ||||
|         tmpdir.join("setup.cfg").write(py.code.Source(""" | ||||
|         tmpdir.join("setup.cfg").write(_pytest._code.Source(""" | ||||
|             [pytest] | ||||
|             addopts = --verbose | ||||
|         """)) | ||||
|  | @ -296,7 +297,7 @@ class TestConfigFromdictargs: | |||
|         assert config.option.capture == 'no' | ||||
| 
 | ||||
|     def test_inifilename(self, tmpdir): | ||||
|         tmpdir.join("foo/bar.ini").ensure().write(py.code.Source(""" | ||||
|         tmpdir.join("foo/bar.ini").ensure().write(_pytest._code.Source(""" | ||||
|             [pytest] | ||||
|             name = value | ||||
|         """)) | ||||
|  | @ -309,7 +310,7 @@ class TestConfigFromdictargs: | |||
|         } | ||||
| 
 | ||||
|         cwd = tmpdir.join('a/b') | ||||
|         cwd.join('pytest.ini').ensure().write(py.code.Source(""" | ||||
|         cwd.join('pytest.ini').ensure().write(_pytest._code.Source(""" | ||||
|             [pytest] | ||||
|             name = wrong-value | ||||
|             should_not_be_set = true | ||||
|  |  | |||
|  | @ -1,5 +1,8 @@ | |||
| from textwrap import dedent | ||||
| import py, pytest | ||||
| 
 | ||||
| import _pytest._code | ||||
| import py | ||||
| import pytest | ||||
| from _pytest.config import PytestPluginManager | ||||
| from _pytest.main import EXIT_NOTESTSCOLLECTED, EXIT_USAGEERROR | ||||
| 
 | ||||
|  | @ -156,7 +159,7 @@ def test_setinitial_conftest_subdirs(testdir, name): | |||
| def test_conftest_confcutdir(testdir): | ||||
|     testdir.makeconftest("assert 0") | ||||
|     x = testdir.mkdir("x") | ||||
|     x.join("conftest.py").write(py.code.Source(""" | ||||
|     x.join("conftest.py").write(_pytest._code.Source(""" | ||||
|         def pytest_addoption(parser): | ||||
|             parser.addoption("--xyz", action="store_true") | ||||
|     """)) | ||||
|  | @ -174,7 +177,7 @@ def test_no_conftest(testdir): | |||
| 
 | ||||
| def test_conftest_existing_resultlog(testdir): | ||||
|     x = testdir.mkdir("tests") | ||||
|     x.join("conftest.py").write(py.code.Source(""" | ||||
|     x.join("conftest.py").write(_pytest._code.Source(""" | ||||
|         def pytest_addoption(parser): | ||||
|             parser.addoption("--xyz", action="store_true") | ||||
|     """)) | ||||
|  | @ -184,7 +187,7 @@ def test_conftest_existing_resultlog(testdir): | |||
| 
 | ||||
| def test_conftest_existing_junitxml(testdir): | ||||
|     x = testdir.mkdir("tests") | ||||
|     x.join("conftest.py").write(py.code.Source(""" | ||||
|     x.join("conftest.py").write(_pytest._code.Source(""" | ||||
|         def pytest_addoption(parser): | ||||
|             parser.addoption("--xyz", action="store_true") | ||||
|     """)) | ||||
|  | @ -361,18 +364,18 @@ def test_search_conftest_up_to_inifile(testdir, confcutdir, passed, error): | |||
|     root = testdir.tmpdir | ||||
|     src = root.join('src').ensure(dir=1) | ||||
|     src.join('pytest.ini').write('[pytest]') | ||||
|     src.join('conftest.py').write(py.code.Source(""" | ||||
|     src.join('conftest.py').write(_pytest._code.Source(""" | ||||
|         import pytest | ||||
|         @pytest.fixture | ||||
|         def fix1(): pass | ||||
|     """)) | ||||
|     src.join('test_foo.py').write(py.code.Source(""" | ||||
|     src.join('test_foo.py').write(_pytest._code.Source(""" | ||||
|         def test_1(fix1): | ||||
|             pass | ||||
|         def test_2(out_of_reach): | ||||
|             pass | ||||
|     """)) | ||||
|     root.join('conftest.py').write(py.code.Source(""" | ||||
|     root.join('conftest.py').write(_pytest._code.Source(""" | ||||
|         import pytest | ||||
|         @pytest.fixture | ||||
|         def out_of_reach(): pass | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| # encoding: utf-8 | ||||
| import sys | ||||
| import _pytest._code | ||||
| from _pytest.doctest import DoctestItem, DoctestModule, DoctestTextfile | ||||
| import py | ||||
| import pytest | ||||
| 
 | ||||
| class TestDoctests: | ||||
|  | @ -181,7 +181,7 @@ class TestDoctests: | |||
|         assert 'text-line-after' not in result.stdout.str() | ||||
| 
 | ||||
|     def test_doctest_linedata_missing(self, testdir): | ||||
|         testdir.tmpdir.join('hello.py').write(py.code.Source(""" | ||||
|         testdir.tmpdir.join('hello.py').write(_pytest._code.Source(""" | ||||
|             class Fun(object): | ||||
|                 @property | ||||
|                 def test(self): | ||||
|  | @ -201,7 +201,7 @@ class TestDoctests: | |||
| 
 | ||||
| 
 | ||||
|     def test_doctest_unex_importerror(self, testdir): | ||||
|         testdir.tmpdir.join("hello.py").write(py.code.Source(""" | ||||
|         testdir.tmpdir.join("hello.py").write(_pytest._code.Source(""" | ||||
|             import asdalsdkjaslkdjasd | ||||
|         """)) | ||||
|         testdir.maketxtfile(""" | ||||
|  | @ -229,7 +229,7 @@ class TestDoctests: | |||
| 
 | ||||
|     def test_doctestmodule_external_and_issue116(self, testdir): | ||||
|         p = testdir.mkpydir("hello") | ||||
|         p.join("__init__.py").write(py.code.Source(""" | ||||
|         p.join("__init__.py").write(_pytest._code.Source(""" | ||||
|             def somefunc(): | ||||
|                 ''' | ||||
|                     >>> i = 0 | ||||
|  |  | |||
|  | @ -1,7 +1,8 @@ | |||
| 
 | ||||
| import py | ||||
| import sys | ||||
| 
 | ||||
| import _pytest._code | ||||
| 
 | ||||
| 
 | ||||
| def runpdb_and_get_report(testdir, source): | ||||
|     p = testdir.makepyfile(source) | ||||
|     result = testdir.runpytest_inprocess("--pdb", p) | ||||
|  | @ -27,7 +28,7 @@ class TestPDB: | |||
|         """) | ||||
|         assert rep.failed | ||||
|         assert len(pdblist) == 1 | ||||
|         tb = py.code.Traceback(pdblist[0][0]) | ||||
|         tb = _pytest._code.Traceback(pdblist[0][0]) | ||||
|         assert tb[-1].name == "test_func" | ||||
| 
 | ||||
|     def test_pdb_on_xfail(self, testdir, pdblist): | ||||
|  |  | |||
|  | @ -1,8 +1,12 @@ | |||
| import py, pytest | ||||
| import os | ||||
| 
 | ||||
| import _pytest._code | ||||
| import py | ||||
| import pytest | ||||
| from _pytest.main import Node, Item, FSCollector | ||||
| from _pytest.resultlog import generic_path, ResultLog, \ | ||||
|         pytest_configure, pytest_unconfigure | ||||
| from _pytest.main import Node, Item, FSCollector | ||||
| 
 | ||||
| 
 | ||||
| def test_generic_path(testdir): | ||||
|     from _pytest.main import Session | ||||
|  | @ -140,7 +144,7 @@ class TestWithFunctionIntegration: | |||
|         try: | ||||
|             raise ValueError | ||||
|         except ValueError: | ||||
|             excinfo = py.code.ExceptionInfo() | ||||
|             excinfo = _pytest._code.ExceptionInfo() | ||||
|         reslog = ResultLog(None, py.io.TextIO()) | ||||
|         reslog.pytest_internalerror(excinfo.getrepr(style=style)) | ||||
|         entry = reslog.logfile.getvalue() | ||||
|  |  | |||
|  | @ -1,6 +1,10 @@ | |||
| from __future__ import with_statement | ||||
| 
 | ||||
| import pytest, py, sys, os | ||||
| import _pytest._code | ||||
| import os | ||||
| import py | ||||
| import pytest | ||||
| import sys | ||||
| from _pytest import runner, main | ||||
| 
 | ||||
| class TestSetupState: | ||||
|  | @ -408,14 +412,14 @@ def test_pytest_exit(): | |||
|     try: | ||||
|         pytest.exit("hello") | ||||
|     except pytest.exit.Exception: | ||||
|         excinfo = py.code.ExceptionInfo() | ||||
|         excinfo = _pytest._code.ExceptionInfo() | ||||
|         assert excinfo.errisinstance(KeyboardInterrupt) | ||||
| 
 | ||||
| def test_pytest_fail(): | ||||
|     try: | ||||
|         pytest.fail("hello") | ||||
|     except pytest.fail.Exception: | ||||
|         excinfo = py.code.ExceptionInfo() | ||||
|         excinfo = _pytest._code.ExceptionInfo() | ||||
|         s = excinfo.exconly(tryshort=True) | ||||
|         assert s.startswith("Failed") | ||||
| 
 | ||||
|  | @ -459,7 +463,7 @@ def test_exception_printing_skip(): | |||
|     try: | ||||
|         pytest.skip("hello") | ||||
|     except pytest.skip.Exception: | ||||
|         excinfo = py.code.ExceptionInfo() | ||||
|         excinfo = _pytest._code.ExceptionInfo() | ||||
|         s = excinfo.exconly(tryshort=True) | ||||
|         assert s.startswith("Skipped") | ||||
| 
 | ||||
|  | @ -488,7 +492,7 @@ def test_importorskip(monkeypatch): | |||
|         mod2 = pytest.importorskip("hello123", minversion="1.3") | ||||
|         assert mod2 == mod | ||||
|     except pytest.skip.Exception: | ||||
|         print(py.code.ExceptionInfo()) | ||||
|         print(_pytest._code.ExceptionInfo()) | ||||
|         pytest.fail("spurious skip") | ||||
| 
 | ||||
| def test_importorskip_imports_last_module_part(): | ||||
|  | @ -505,7 +509,7 @@ def test_importorskip_dev_module(monkeypatch): | |||
|         pytest.raises(pytest.skip.Exception, """ | ||||
|             pytest.importorskip('mockmodule1', minversion='0.14.0')""") | ||||
|     except pytest.skip.Exception: | ||||
|         print(py.code.ExceptionInfo()) | ||||
|         print(_pytest._code.ExceptionInfo()) | ||||
|         pytest.fail("spurious skip") | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -2,15 +2,17 @@ | |||
| terminal reporting of the full testing process. | ||||
| """ | ||||
| import collections | ||||
| import pytest | ||||
| import py | ||||
| import sys | ||||
| 
 | ||||
| import _pytest._pluggy as pluggy | ||||
| import _pytest._code | ||||
| import py | ||||
| import pytest | ||||
| from _pytest import runner | ||||
| from _pytest.main import EXIT_NOTESTSCOLLECTED | ||||
| from _pytest.terminal import TerminalReporter, repr_pythonversion, getreportopt | ||||
| from _pytest.terminal import build_summary_stats_line, _plugin_nameversions | ||||
| from _pytest import runner | ||||
| import _pytest._pluggy as pluggy | ||||
| 
 | ||||
| 
 | ||||
| def basic_run_report(item): | ||||
|     runner.call_and_report(item, "setup", log=False) | ||||
|  | @ -153,7 +155,7 @@ class TestTerminal: | |||
| 
 | ||||
|     def test_itemreport_directclasses_not_shown_as_subclasses(self, testdir): | ||||
|         a = testdir.mkpydir("a123") | ||||
|         a.join("test_hello123.py").write(py.code.Source(""" | ||||
|         a.join("test_hello123.py").write(_pytest._code.Source(""" | ||||
|             class TestClass: | ||||
|                 def test_method(self): | ||||
|                     pass | ||||
|  | @ -268,7 +270,7 @@ class TestCollectonly: | |||
|         p = testdir.makepyfile("import Errlkjqweqwe") | ||||
|         result = testdir.runpytest("--collect-only", p) | ||||
|         assert result.ret == 1 | ||||
|         result.stdout.fnmatch_lines(py.code.Source(""" | ||||
|         result.stdout.fnmatch_lines(_pytest._code.Source(""" | ||||
|             *ERROR* | ||||
|             *import Errlk* | ||||
|             *ImportError* | ||||
|  |  | |||
|  | @ -260,6 +260,7 @@ def test_testcase_custom_exception_info(testdir, type): | |||
|     testdir.makepyfile(""" | ||||
|         from unittest import TestCase | ||||
|         import py, pytest | ||||
|         import _pytest._code | ||||
|         class MyTestCase(TestCase): | ||||
|             def run(self, result): | ||||
|                 excinfo = pytest.raises(ZeroDivisionError, lambda: 0/0) | ||||
|  | @ -269,7 +270,7 @@ def test_testcase_custom_exception_info(testdir, type): | |||
|                 def t(*args): | ||||
|                     mp.undo() | ||||
|                     raise TypeError() | ||||
|                 mp.setattr(py.code, 'ExceptionInfo', t) | ||||
|                 mp.setattr(_pytest._code, 'ExceptionInfo', t) | ||||
|                 try: | ||||
|                     excinfo = excinfo._excinfo | ||||
|                     result.add%(type)s(self, excinfo) | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue