eggs/py-1.4.0-py2.6.egg/py/_code/_assertionold.py
changeset 69 c6bca38c1cbf
equal deleted inserted replaced
68:5ff1fc726848 69:c6bca38c1cbf
       
     1 import py
       
     2 import sys, inspect
       
     3 from compiler import parse, ast, pycodegen
       
     4 from py._code.assertion import BuiltinAssertionError, _format_explanation
       
     5 
       
     6 passthroughex = py.builtin._sysex
       
     7 
       
     8 class Failure:
       
     9     def __init__(self, node):
       
    10         self.exc, self.value, self.tb = sys.exc_info()
       
    11         self.node = node
       
    12 
       
    13 class View(object):
       
    14     """View base class.
       
    15 
       
    16     If C is a subclass of View, then C(x) creates a proxy object around
       
    17     the object x.  The actual class of the proxy is not C in general,
       
    18     but a *subclass* of C determined by the rules below.  To avoid confusion
       
    19     we call view class the class of the proxy (a subclass of C, so of View)
       
    20     and object class the class of x.
       
    21 
       
    22     Attributes and methods not found in the proxy are automatically read on x.
       
    23     Other operations like setting attributes are performed on the proxy, as
       
    24     determined by its view class.  The object x is available from the proxy
       
    25     as its __obj__ attribute.
       
    26 
       
    27     The view class selection is determined by the __view__ tuples and the
       
    28     optional __viewkey__ method.  By default, the selected view class is the
       
    29     most specific subclass of C whose __view__ mentions the class of x.
       
    30     If no such subclass is found, the search proceeds with the parent
       
    31     object classes.  For example, C(True) will first look for a subclass
       
    32     of C with __view__ = (..., bool, ...) and only if it doesn't find any
       
    33     look for one with __view__ = (..., int, ...), and then ..., object,...
       
    34     If everything fails the class C itself is considered to be the default.
       
    35 
       
    36     Alternatively, the view class selection can be driven by another aspect
       
    37     of the object x, instead of the class of x, by overriding __viewkey__.
       
    38     See last example at the end of this module.
       
    39     """
       
    40 
       
    41     _viewcache = {}
       
    42     __view__ = ()
       
    43 
       
    44     def __new__(rootclass, obj, *args, **kwds):
       
    45         self = object.__new__(rootclass)
       
    46         self.__obj__ = obj
       
    47         self.__rootclass__ = rootclass
       
    48         key = self.__viewkey__()
       
    49         try:
       
    50             self.__class__ = self._viewcache[key]
       
    51         except KeyError:
       
    52             self.__class__ = self._selectsubclass(key)
       
    53         return self
       
    54 
       
    55     def __getattr__(self, attr):
       
    56         # attributes not found in the normal hierarchy rooted on View
       
    57         # are looked up in the object's real class
       
    58         return getattr(self.__obj__, attr)
       
    59 
       
    60     def __viewkey__(self):
       
    61         return self.__obj__.__class__
       
    62 
       
    63     def __matchkey__(self, key, subclasses):
       
    64         if inspect.isclass(key):
       
    65             keys = inspect.getmro(key)
       
    66         else:
       
    67             keys = [key]
       
    68         for key in keys:
       
    69             result = [C for C in subclasses if key in C.__view__]
       
    70             if result:
       
    71                 return result
       
    72         return []
       
    73 
       
    74     def _selectsubclass(self, key):
       
    75         subclasses = list(enumsubclasses(self.__rootclass__))
       
    76         for C in subclasses:
       
    77             if not isinstance(C.__view__, tuple):
       
    78                 C.__view__ = (C.__view__,)
       
    79         choices = self.__matchkey__(key, subclasses)
       
    80         if not choices:
       
    81             return self.__rootclass__
       
    82         elif len(choices) == 1:
       
    83             return choices[0]
       
    84         else:
       
    85             # combine the multiple choices
       
    86             return type('?', tuple(choices), {})
       
    87 
       
    88     def __repr__(self):
       
    89         return '%s(%r)' % (self.__rootclass__.__name__, self.__obj__)
       
    90 
       
    91 
       
    92 def enumsubclasses(cls):
       
    93     for subcls in cls.__subclasses__():
       
    94         for subsubclass in enumsubclasses(subcls):
       
    95             yield subsubclass
       
    96     yield cls
       
    97 
       
    98 
       
    99 class Interpretable(View):
       
   100     """A parse tree node with a few extra methods."""
       
   101     explanation = None
       
   102 
       
   103     def is_builtin(self, frame):
       
   104         return False
       
   105 
       
   106     def eval(self, frame):
       
   107         # fall-back for unknown expression nodes
       
   108         try:
       
   109             expr = ast.Expression(self.__obj__)
       
   110             expr.filename = '<eval>'
       
   111             self.__obj__.filename = '<eval>'
       
   112             co = pycodegen.ExpressionCodeGenerator(expr).getCode()
       
   113             result = frame.eval(co)
       
   114         except passthroughex:
       
   115             raise
       
   116         except:
       
   117             raise Failure(self)
       
   118         self.result = result
       
   119         self.explanation = self.explanation or frame.repr(self.result)
       
   120 
       
   121     def run(self, frame):
       
   122         # fall-back for unknown statement nodes
       
   123         try:
       
   124             expr = ast.Module(None, ast.Stmt([self.__obj__]))
       
   125             expr.filename = '<run>'
       
   126             co = pycodegen.ModuleCodeGenerator(expr).getCode()
       
   127             frame.exec_(co)
       
   128         except passthroughex:
       
   129             raise
       
   130         except:
       
   131             raise Failure(self)
       
   132 
       
   133     def nice_explanation(self):
       
   134         return _format_explanation(self.explanation)
       
   135 
       
   136 
       
   137 class Name(Interpretable):
       
   138     __view__ = ast.Name
       
   139 
       
   140     def is_local(self, frame):
       
   141         source = '%r in locals() is not globals()' % self.name
       
   142         try:
       
   143             return frame.is_true(frame.eval(source))
       
   144         except passthroughex:
       
   145             raise
       
   146         except:
       
   147             return False
       
   148 
       
   149     def is_global(self, frame):
       
   150         source = '%r in globals()' % self.name
       
   151         try:
       
   152             return frame.is_true(frame.eval(source))
       
   153         except passthroughex:
       
   154             raise
       
   155         except:
       
   156             return False
       
   157 
       
   158     def is_builtin(self, frame):
       
   159         source = '%r not in locals() and %r not in globals()' % (
       
   160             self.name, self.name)
       
   161         try:
       
   162             return frame.is_true(frame.eval(source))
       
   163         except passthroughex:
       
   164             raise
       
   165         except:
       
   166             return False
       
   167 
       
   168     def eval(self, frame):
       
   169         super(Name, self).eval(frame)
       
   170         if not self.is_local(frame):
       
   171             self.explanation = self.name
       
   172 
       
   173 class Compare(Interpretable):
       
   174     __view__ = ast.Compare
       
   175 
       
   176     def eval(self, frame):
       
   177         expr = Interpretable(self.expr)
       
   178         expr.eval(frame)
       
   179         for operation, expr2 in self.ops:
       
   180             if hasattr(self, 'result'):
       
   181                 # shortcutting in chained expressions
       
   182                 if not frame.is_true(self.result):
       
   183                     break
       
   184             expr2 = Interpretable(expr2)
       
   185             expr2.eval(frame)
       
   186             self.explanation = "%s %s %s" % (
       
   187                 expr.explanation, operation, expr2.explanation)
       
   188             source = "__exprinfo_left %s __exprinfo_right" % operation
       
   189             try:
       
   190                 self.result = frame.eval(source,
       
   191                                          __exprinfo_left=expr.result,
       
   192                                          __exprinfo_right=expr2.result)
       
   193             except passthroughex:
       
   194                 raise
       
   195             except:
       
   196                 raise Failure(self)
       
   197             expr = expr2
       
   198 
       
   199 class And(Interpretable):
       
   200     __view__ = ast.And
       
   201 
       
   202     def eval(self, frame):
       
   203         explanations = []
       
   204         for expr in self.nodes:
       
   205             expr = Interpretable(expr)
       
   206             expr.eval(frame)
       
   207             explanations.append(expr.explanation)
       
   208             self.result = expr.result
       
   209             if not frame.is_true(expr.result):
       
   210                 break
       
   211         self.explanation = '(' + ' and '.join(explanations) + ')'
       
   212 
       
   213 class Or(Interpretable):
       
   214     __view__ = ast.Or
       
   215 
       
   216     def eval(self, frame):
       
   217         explanations = []
       
   218         for expr in self.nodes:
       
   219             expr = Interpretable(expr)
       
   220             expr.eval(frame)
       
   221             explanations.append(expr.explanation)
       
   222             self.result = expr.result
       
   223             if frame.is_true(expr.result):
       
   224                 break
       
   225         self.explanation = '(' + ' or '.join(explanations) + ')'
       
   226 
       
   227 
       
   228 # == Unary operations ==
       
   229 keepalive = []
       
   230 for astclass, astpattern in {
       
   231     ast.Not    : 'not __exprinfo_expr',
       
   232     ast.Invert : '(~__exprinfo_expr)',
       
   233     }.items():
       
   234 
       
   235     class UnaryArith(Interpretable):
       
   236         __view__ = astclass
       
   237 
       
   238         def eval(self, frame, astpattern=astpattern):
       
   239             expr = Interpretable(self.expr)
       
   240             expr.eval(frame)
       
   241             self.explanation = astpattern.replace('__exprinfo_expr',
       
   242                                                   expr.explanation)
       
   243             try:
       
   244                 self.result = frame.eval(astpattern,
       
   245                                          __exprinfo_expr=expr.result)
       
   246             except passthroughex:
       
   247                 raise
       
   248             except:
       
   249                 raise Failure(self)
       
   250 
       
   251     keepalive.append(UnaryArith)
       
   252 
       
   253 # == Binary operations ==
       
   254 for astclass, astpattern in {
       
   255     ast.Add    : '(__exprinfo_left + __exprinfo_right)',
       
   256     ast.Sub    : '(__exprinfo_left - __exprinfo_right)',
       
   257     ast.Mul    : '(__exprinfo_left * __exprinfo_right)',
       
   258     ast.Div    : '(__exprinfo_left / __exprinfo_right)',
       
   259     ast.Mod    : '(__exprinfo_left % __exprinfo_right)',
       
   260     ast.Power  : '(__exprinfo_left ** __exprinfo_right)',
       
   261     }.items():
       
   262 
       
   263     class BinaryArith(Interpretable):
       
   264         __view__ = astclass
       
   265 
       
   266         def eval(self, frame, astpattern=astpattern):
       
   267             left = Interpretable(self.left)
       
   268             left.eval(frame)
       
   269             right = Interpretable(self.right)
       
   270             right.eval(frame)
       
   271             self.explanation = (astpattern
       
   272                                 .replace('__exprinfo_left',  left .explanation)
       
   273                                 .replace('__exprinfo_right', right.explanation))
       
   274             try:
       
   275                 self.result = frame.eval(astpattern,
       
   276                                          __exprinfo_left=left.result,
       
   277                                          __exprinfo_right=right.result)
       
   278             except passthroughex:
       
   279                 raise
       
   280             except:
       
   281                 raise Failure(self)
       
   282 
       
   283     keepalive.append(BinaryArith)
       
   284 
       
   285 
       
   286 class CallFunc(Interpretable):
       
   287     __view__ = ast.CallFunc
       
   288 
       
   289     def is_bool(self, frame):
       
   290         source = 'isinstance(__exprinfo_value, bool)'
       
   291         try:
       
   292             return frame.is_true(frame.eval(source,
       
   293                                             __exprinfo_value=self.result))
       
   294         except passthroughex:
       
   295             raise
       
   296         except:
       
   297             return False
       
   298 
       
   299     def eval(self, frame):
       
   300         node = Interpretable(self.node)
       
   301         node.eval(frame)
       
   302         explanations = []
       
   303         vars = {'__exprinfo_fn': node.result}
       
   304         source = '__exprinfo_fn('
       
   305         for a in self.args:
       
   306             if isinstance(a, ast.Keyword):
       
   307                 keyword = a.name
       
   308                 a = a.expr
       
   309             else:
       
   310                 keyword = None
       
   311             a = Interpretable(a)
       
   312             a.eval(frame)
       
   313             argname = '__exprinfo_%d' % len(vars)
       
   314             vars[argname] = a.result
       
   315             if keyword is None:
       
   316                 source += argname + ','
       
   317                 explanations.append(a.explanation)
       
   318             else:
       
   319                 source += '%s=%s,' % (keyword, argname)
       
   320                 explanations.append('%s=%s' % (keyword, a.explanation))
       
   321         if self.star_args:
       
   322             star_args = Interpretable(self.star_args)
       
   323             star_args.eval(frame)
       
   324             argname = '__exprinfo_star'
       
   325             vars[argname] = star_args.result
       
   326             source += '*' + argname + ','
       
   327             explanations.append('*' + star_args.explanation)
       
   328         if self.dstar_args:
       
   329             dstar_args = Interpretable(self.dstar_args)
       
   330             dstar_args.eval(frame)
       
   331             argname = '__exprinfo_kwds'
       
   332             vars[argname] = dstar_args.result
       
   333             source += '**' + argname + ','
       
   334             explanations.append('**' + dstar_args.explanation)
       
   335         self.explanation = "%s(%s)" % (
       
   336             node.explanation, ', '.join(explanations))
       
   337         if source.endswith(','):
       
   338             source = source[:-1]
       
   339         source += ')'
       
   340         try:
       
   341             self.result = frame.eval(source, **vars)
       
   342         except passthroughex:
       
   343             raise
       
   344         except:
       
   345             raise Failure(self)
       
   346         if not node.is_builtin(frame) or not self.is_bool(frame):
       
   347             r = frame.repr(self.result)
       
   348             self.explanation = '%s\n{%s = %s\n}' % (r, r, self.explanation)
       
   349 
       
   350 class Getattr(Interpretable):
       
   351     __view__ = ast.Getattr
       
   352 
       
   353     def eval(self, frame):
       
   354         expr = Interpretable(self.expr)
       
   355         expr.eval(frame)
       
   356         source = '__exprinfo_expr.%s' % self.attrname
       
   357         try:
       
   358             self.result = frame.eval(source, __exprinfo_expr=expr.result)
       
   359         except passthroughex:
       
   360             raise
       
   361         except:
       
   362             raise Failure(self)
       
   363         self.explanation = '%s.%s' % (expr.explanation, self.attrname)
       
   364         # if the attribute comes from the instance, its value is interesting
       
   365         source = ('hasattr(__exprinfo_expr, "__dict__") and '
       
   366                   '%r in __exprinfo_expr.__dict__' % self.attrname)
       
   367         try:
       
   368             from_instance = frame.is_true(
       
   369                 frame.eval(source, __exprinfo_expr=expr.result))
       
   370         except passthroughex:
       
   371             raise
       
   372         except:
       
   373             from_instance = True
       
   374         if from_instance:
       
   375             r = frame.repr(self.result)
       
   376             self.explanation = '%s\n{%s = %s\n}' % (r, r, self.explanation)
       
   377 
       
   378 # == Re-interpretation of full statements ==
       
   379 
       
   380 class Assert(Interpretable):
       
   381     __view__ = ast.Assert
       
   382 
       
   383     def run(self, frame):
       
   384         test = Interpretable(self.test)
       
   385         test.eval(frame)
       
   386         # simplify 'assert False where False = ...'
       
   387         if (test.explanation.startswith('False\n{False = ') and
       
   388             test.explanation.endswith('\n}')):
       
   389             test.explanation = test.explanation[15:-2]
       
   390         # print the result as  'assert <explanation>'
       
   391         self.result = test.result
       
   392         self.explanation = 'assert ' + test.explanation
       
   393         if not frame.is_true(test.result):
       
   394             try:
       
   395                 raise BuiltinAssertionError
       
   396             except passthroughex:
       
   397                 raise
       
   398             except:
       
   399                 raise Failure(self)
       
   400 
       
   401 class Assign(Interpretable):
       
   402     __view__ = ast.Assign
       
   403 
       
   404     def run(self, frame):
       
   405         expr = Interpretable(self.expr)
       
   406         expr.eval(frame)
       
   407         self.result = expr.result
       
   408         self.explanation = '... = ' + expr.explanation
       
   409         # fall-back-run the rest of the assignment
       
   410         ass = ast.Assign(self.nodes, ast.Name('__exprinfo_expr'))
       
   411         mod = ast.Module(None, ast.Stmt([ass]))
       
   412         mod.filename = '<run>'
       
   413         co = pycodegen.ModuleCodeGenerator(mod).getCode()
       
   414         try:
       
   415             frame.exec_(co, __exprinfo_expr=expr.result)
       
   416         except passthroughex:
       
   417             raise
       
   418         except:
       
   419             raise Failure(self)
       
   420 
       
   421 class Discard(Interpretable):
       
   422     __view__ = ast.Discard
       
   423 
       
   424     def run(self, frame):
       
   425         expr = Interpretable(self.expr)
       
   426         expr.eval(frame)
       
   427         self.result = expr.result
       
   428         self.explanation = expr.explanation
       
   429 
       
   430 class Stmt(Interpretable):
       
   431     __view__ = ast.Stmt
       
   432 
       
   433     def run(self, frame):
       
   434         for stmt in self.nodes:
       
   435             stmt = Interpretable(stmt)
       
   436             stmt.run(frame)
       
   437 
       
   438 
       
   439 def report_failure(e):
       
   440     explanation = e.node.nice_explanation()
       
   441     if explanation:
       
   442         explanation = ", in: " + explanation
       
   443     else:
       
   444         explanation = ""
       
   445     sys.stdout.write("%s: %s%s\n" % (e.exc.__name__, e.value, explanation))
       
   446 
       
   447 def check(s, frame=None):
       
   448     if frame is None:
       
   449         frame = sys._getframe(1)
       
   450         frame = py.code.Frame(frame)
       
   451     expr = parse(s, 'eval')
       
   452     assert isinstance(expr, ast.Expression)
       
   453     node = Interpretable(expr.node)
       
   454     try:
       
   455         node.eval(frame)
       
   456     except passthroughex:
       
   457         raise
       
   458     except Failure:
       
   459         e = sys.exc_info()[1]
       
   460         report_failure(e)
       
   461     else:
       
   462         if not frame.is_true(node.result):
       
   463             sys.stderr.write("assertion failed: %s\n" % node.nice_explanation())
       
   464 
       
   465 
       
   466 ###########################################################
       
   467 # API / Entry points
       
   468 # #########################################################
       
   469 
       
   470 def interpret(source, frame, should_fail=False):
       
   471     module = Interpretable(parse(source, 'exec').node)
       
   472     #print "got module", module
       
   473     if isinstance(frame, py.std.types.FrameType):
       
   474         frame = py.code.Frame(frame)
       
   475     try:
       
   476         module.run(frame)
       
   477     except Failure:
       
   478         e = sys.exc_info()[1]
       
   479         return getfailure(e)
       
   480     except passthroughex:
       
   481         raise
       
   482     except:
       
   483         import traceback
       
   484         traceback.print_exc()
       
   485     if should_fail:
       
   486         return ("(assertion failed, but when it was re-run for "
       
   487                 "printing intermediate values, it did not fail.  Suggestions: "
       
   488                 "compute assert expression before the assert or use --nomagic)")
       
   489     else:
       
   490         return None
       
   491 
       
   492 def getmsg(excinfo):
       
   493     if isinstance(excinfo, tuple):
       
   494         excinfo = py.code.ExceptionInfo(excinfo)
       
   495     #frame, line = gettbline(tb)
       
   496     #frame = py.code.Frame(frame)
       
   497     #return interpret(line, frame)
       
   498 
       
   499     tb = excinfo.traceback[-1]
       
   500     source = str(tb.statement).strip()
       
   501     x = interpret(source, tb.frame, should_fail=True)
       
   502     if not isinstance(x, str):
       
   503         raise TypeError("interpret returned non-string %r" % (x,))
       
   504     return x
       
   505 
       
   506 def getfailure(e):
       
   507     explanation = e.node.nice_explanation()
       
   508     if str(e.value):
       
   509         lines = explanation.split('\n')
       
   510         lines[0] += "  << %s" % (e.value,)
       
   511         explanation = '\n'.join(lines)
       
   512     text = "%s: %s" % (e.exc.__name__, explanation)
       
   513     if text.startswith('AssertionError: assert '):
       
   514         text = text[16:]
       
   515     return text
       
   516 
       
   517 def run(s, frame=None):
       
   518     if frame is None:
       
   519         frame = sys._getframe(1)
       
   520         frame = py.code.Frame(frame)
       
   521     module = Interpretable(parse(s, 'exec').node)
       
   522     try:
       
   523         module.run(frame)
       
   524     except Failure:
       
   525         e = sys.exc_info()[1]
       
   526         report_failure(e)
       
   527 
       
   528 
       
   529 if __name__ == '__main__':
       
   530     # example:
       
   531     def f():
       
   532         return 5
       
   533     def g():
       
   534         return 3
       
   535     def h(x):
       
   536         return 'never'
       
   537     check("f() * g() == 5")
       
   538     check("not f()")
       
   539     check("not (f() and g() or 0)")
       
   540     check("f() == g()")
       
   541     i = 4
       
   542     check("i == f()")
       
   543     check("len(f()) == 0")
       
   544     check("isinstance(2+3+4, float)")
       
   545 
       
   546     run("x = i")
       
   547     check("x == 5")
       
   548 
       
   549     run("assert not f(), 'oops'")
       
   550     run("a, b, c = 1, 2")
       
   551     run("a, b, c = f()")
       
   552 
       
   553     check("max([f(),g()]) == 4")
       
   554     check("'hello'[g()] == 'h'")
       
   555     run("'guk%d' % h(f())")