|
1 import sys |
|
2 import py |
|
3 |
|
4 BuiltinAssertionError = py.builtin.builtins.AssertionError |
|
5 |
|
6 _reprcompare = None # if set, will be called by assert reinterp for comparison ops |
|
7 |
|
8 def _format_explanation(explanation): |
|
9 """This formats an explanation |
|
10 |
|
11 Normally all embedded newlines are escaped, however there are |
|
12 three exceptions: \n{, \n} and \n~. The first two are intended |
|
13 cover nested explanations, see function and attribute explanations |
|
14 for examples (.visit_Call(), visit_Attribute()). The last one is |
|
15 for when one explanation needs to span multiple lines, e.g. when |
|
16 displaying diffs. |
|
17 """ |
|
18 raw_lines = (explanation or '').split('\n') |
|
19 # escape newlines not followed by {, } and ~ |
|
20 lines = [raw_lines[0]] |
|
21 for l in raw_lines[1:]: |
|
22 if l.startswith('{') or l.startswith('}') or l.startswith('~'): |
|
23 lines.append(l) |
|
24 else: |
|
25 lines[-1] += '\\n' + l |
|
26 |
|
27 result = lines[:1] |
|
28 stack = [0] |
|
29 stackcnt = [0] |
|
30 for line in lines[1:]: |
|
31 if line.startswith('{'): |
|
32 if stackcnt[-1]: |
|
33 s = 'and ' |
|
34 else: |
|
35 s = 'where ' |
|
36 stack.append(len(result)) |
|
37 stackcnt[-1] += 1 |
|
38 stackcnt.append(0) |
|
39 result.append(' +' + ' '*(len(stack)-1) + s + line[1:]) |
|
40 elif line.startswith('}'): |
|
41 assert line.startswith('}') |
|
42 stack.pop() |
|
43 stackcnt.pop() |
|
44 result[stack[-1]] += line[1:] |
|
45 else: |
|
46 assert line.startswith('~') |
|
47 result.append(' '*len(stack) + line[1:]) |
|
48 assert len(stack) == 1 |
|
49 return '\n'.join(result) |
|
50 |
|
51 |
|
52 class AssertionError(BuiltinAssertionError): |
|
53 def __init__(self, *args): |
|
54 BuiltinAssertionError.__init__(self, *args) |
|
55 if args: |
|
56 try: |
|
57 self.msg = str(args[0]) |
|
58 except py.builtin._sysex: |
|
59 raise |
|
60 except: |
|
61 self.msg = "<[broken __repr__] %s at %0xd>" %( |
|
62 args[0].__class__, id(args[0])) |
|
63 else: |
|
64 f = py.code.Frame(sys._getframe(1)) |
|
65 try: |
|
66 source = f.code.fullsource |
|
67 if source is not None: |
|
68 try: |
|
69 source = source.getstatement(f.lineno, assertion=True) |
|
70 except IndexError: |
|
71 source = None |
|
72 else: |
|
73 source = str(source.deindent()).strip() |
|
74 except py.error.ENOENT: |
|
75 source = None |
|
76 # this can also occur during reinterpretation, when the |
|
77 # co_filename is set to "<run>". |
|
78 if source: |
|
79 self.msg = reinterpret(source, f, should_fail=True) |
|
80 else: |
|
81 self.msg = "<could not determine information>" |
|
82 if not self.args: |
|
83 self.args = (self.msg,) |
|
84 |
|
85 if sys.version_info > (3, 0): |
|
86 AssertionError.__module__ = "builtins" |
|
87 reinterpret_old = "old reinterpretation not available for py3" |
|
88 else: |
|
89 from py._code._assertionold import interpret as reinterpret_old |
|
90 if sys.version_info >= (2, 6) or (sys.platform.startswith("java")): |
|
91 from py._code._assertionnew import interpret as reinterpret |
|
92 else: |
|
93 reinterpret = reinterpret_old |
|
94 |