##// END OF EJS Templates
Fix some test on python 3.9 (nightly)....
Matthias Bussonnier -
Show More
@@ -1,313 +1,316 b''
1 """
1 """
2 Test for async helpers.
2 Test for async helpers.
3
3
4 Should only trigger on python 3.5+ or will have syntax errors.
4 Should only trigger on python 3.5+ or will have syntax errors.
5 """
5 """
6 from itertools import chain, repeat
6 from itertools import chain, repeat
7 import nose.tools as nt
7 import nose.tools as nt
8 from textwrap import dedent, indent
8 from textwrap import dedent, indent
9 from unittest import TestCase
9 from unittest import TestCase
10 from IPython.testing.decorators import skip_without
10 from IPython.testing.decorators import skip_without
11
11 import sys
12
12
13 iprc = lambda x: ip.run_cell(dedent(x)).raise_error()
13 iprc = lambda x: ip.run_cell(dedent(x)).raise_error()
14 iprc_nr = lambda x: ip.run_cell(dedent(x))
14 iprc_nr = lambda x: ip.run_cell(dedent(x))
15
15
16 from IPython.core.async_helpers import _should_be_async
16 from IPython.core.async_helpers import _should_be_async
17
17
18 class AsyncTest(TestCase):
18 class AsyncTest(TestCase):
19 def test_should_be_async(self):
19 def test_should_be_async(self):
20 nt.assert_false(_should_be_async("False"))
20 nt.assert_false(_should_be_async("False"))
21 nt.assert_true(_should_be_async("await bar()"))
21 nt.assert_true(_should_be_async("await bar()"))
22 nt.assert_true(_should_be_async("x = await bar()"))
22 nt.assert_true(_should_be_async("x = await bar()"))
23 nt.assert_false(
23 nt.assert_false(
24 _should_be_async(
24 _should_be_async(
25 dedent(
25 dedent(
26 """
26 """
27 async def awaitable():
27 async def awaitable():
28 pass
28 pass
29 """
29 """
30 )
30 )
31 )
31 )
32 )
32 )
33
33
34 def _get_top_level_cases(self):
34 def _get_top_level_cases(self):
35 # These are test cases that should be valid in a function
35 # These are test cases that should be valid in a function
36 # but invalid outside of a function.
36 # but invalid outside of a function.
37 test_cases = []
37 test_cases = []
38 test_cases.append(('basic', "{val}"))
38 test_cases.append(('basic', "{val}"))
39
39
40 # Note, in all conditional cases, I use True instead of
40 # Note, in all conditional cases, I use True instead of
41 # False so that the peephole optimizer won't optimize away
41 # False so that the peephole optimizer won't optimize away
42 # the return, so CPython will see this as a syntax error:
42 # the return, so CPython will see this as a syntax error:
43 #
43 #
44 # while True:
44 # while True:
45 # break
45 # break
46 # return
46 # return
47 #
47 #
48 # But not this:
48 # But not this:
49 #
49 #
50 # while False:
50 # while False:
51 # return
51 # return
52 #
52 #
53 # See https://bugs.python.org/issue1875
53 # See https://bugs.python.org/issue1875
54
54
55 test_cases.append(('if', dedent("""
55 test_cases.append(('if', dedent("""
56 if True:
56 if True:
57 {val}
57 {val}
58 """)))
58 """)))
59
59
60 test_cases.append(('while', dedent("""
60 test_cases.append(('while', dedent("""
61 while True:
61 while True:
62 {val}
62 {val}
63 break
63 break
64 """)))
64 """)))
65
65
66 test_cases.append(('try', dedent("""
66 test_cases.append(('try', dedent("""
67 try:
67 try:
68 {val}
68 {val}
69 except:
69 except:
70 pass
70 pass
71 """)))
71 """)))
72
72
73 test_cases.append(('except', dedent("""
73 test_cases.append(('except', dedent("""
74 try:
74 try:
75 pass
75 pass
76 except:
76 except:
77 {val}
77 {val}
78 """)))
78 """)))
79
79
80 test_cases.append(('finally', dedent("""
80 test_cases.append(('finally', dedent("""
81 try:
81 try:
82 pass
82 pass
83 except:
83 except:
84 pass
84 pass
85 finally:
85 finally:
86 {val}
86 {val}
87 """)))
87 """)))
88
88
89 test_cases.append(('for', dedent("""
89 test_cases.append(('for', dedent("""
90 for _ in range(4):
90 for _ in range(4):
91 {val}
91 {val}
92 """)))
92 """)))
93
93
94
94
95 test_cases.append(('nested', dedent("""
95 test_cases.append(('nested', dedent("""
96 if True:
96 if True:
97 while True:
97 while True:
98 {val}
98 {val}
99 break
99 break
100 """)))
100 """)))
101
101
102 test_cases.append(('deep-nested', dedent("""
102 test_cases.append(('deep-nested', dedent("""
103 if True:
103 if True:
104 while True:
104 while True:
105 break
105 break
106 for x in range(3):
106 for x in range(3):
107 if True:
107 if True:
108 while True:
108 while True:
109 for x in range(3):
109 for x in range(3):
110 {val}
110 {val}
111 """)))
111 """)))
112
112
113 return test_cases
113 return test_cases
114
114
115 def _get_ry_syntax_errors(self):
115 def _get_ry_syntax_errors(self):
116 # This is a mix of tests that should be a syntax error if
116 # This is a mix of tests that should be a syntax error if
117 # return or yield whether or not they are in a function
117 # return or yield whether or not they are in a function
118
118
119 test_cases = []
119 test_cases = []
120
120
121 test_cases.append(('class', dedent("""
121 test_cases.append(('class', dedent("""
122 class V:
122 class V:
123 {val}
123 {val}
124 """)))
124 """)))
125
125
126 test_cases.append(('nested-class', dedent("""
126 test_cases.append(('nested-class', dedent("""
127 class V:
127 class V:
128 class C:
128 class C:
129 {val}
129 {val}
130 """)))
130 """)))
131
131
132 return test_cases
132 return test_cases
133
133
134
134
135 def test_top_level_return_error(self):
135 def test_top_level_return_error(self):
136 tl_err_test_cases = self._get_top_level_cases()
136 tl_err_test_cases = self._get_top_level_cases()
137 tl_err_test_cases.extend(self._get_ry_syntax_errors())
137 tl_err_test_cases.extend(self._get_ry_syntax_errors())
138
138
139 vals = ('return', 'yield', 'yield from (_ for _ in range(3))',
139 vals = ('return', 'yield', 'yield from (_ for _ in range(3))',
140 dedent('''
140 dedent('''
141 def f():
141 def f():
142 pass
142 pass
143 return
143 return
144 '''),
144 '''),
145 )
145 )
146
146
147 for test_name, test_case in tl_err_test_cases:
147 for test_name, test_case in tl_err_test_cases:
148 # This example should work if 'pass' is used as the value
148 # This example should work if 'pass' is used as the value
149 with self.subTest((test_name, 'pass')):
149 with self.subTest((test_name, 'pass')):
150 iprc(test_case.format(val='pass'))
150 iprc(test_case.format(val='pass'))
151
151
152 # It should fail with all the values
152 # It should fail with all the values
153 for val in vals:
153 for val in vals:
154 with self.subTest((test_name, val)):
154 with self.subTest((test_name, val)):
155 msg = "Syntax error not raised for %s, %s" % (test_name, val)
155 msg = "Syntax error not raised for %s, %s" % (test_name, val)
156 with self.assertRaises(SyntaxError, msg=msg):
156 with self.assertRaises(SyntaxError, msg=msg):
157 iprc(test_case.format(val=val))
157 iprc(test_case.format(val=val))
158
158
159 def test_in_func_no_error(self):
159 def test_in_func_no_error(self):
160 # Test that the implementation of top-level return/yield
160 # Test that the implementation of top-level return/yield
161 # detection isn't *too* aggressive, and works inside a function
161 # detection isn't *too* aggressive, and works inside a function
162 func_contexts = []
162 func_contexts = []
163
163
164 func_contexts.append(('func', False, dedent("""
164 func_contexts.append(('func', False, dedent("""
165 def f():""")))
165 def f():""")))
166
166
167 func_contexts.append(('method', False, dedent("""
167 func_contexts.append(('method', False, dedent("""
168 class MyClass:
168 class MyClass:
169 def __init__(self):
169 def __init__(self):
170 """)))
170 """)))
171
171
172 func_contexts.append(('async-func', True, dedent("""
172 func_contexts.append(('async-func', True, dedent("""
173 async def f():""")))
173 async def f():""")))
174
174
175 func_contexts.append(('async-method', True, dedent("""
175 func_contexts.append(('async-method', True, dedent("""
176 class MyClass:
176 class MyClass:
177 async def f(self):""")))
177 async def f(self):""")))
178
178
179 func_contexts.append(('closure', False, dedent("""
179 func_contexts.append(('closure', False, dedent("""
180 def f():
180 def f():
181 def g():
181 def g():
182 """)))
182 """)))
183
183
184 def nest_case(context, case):
184 def nest_case(context, case):
185 # Detect indentation
185 # Detect indentation
186 lines = context.strip().splitlines()
186 lines = context.strip().splitlines()
187 prefix_len = 0
187 prefix_len = 0
188 for c in lines[-1]:
188 for c in lines[-1]:
189 if c != ' ':
189 if c != ' ':
190 break
190 break
191 prefix_len += 1
191 prefix_len += 1
192
192
193 indented_case = indent(case, ' ' * (prefix_len + 4))
193 indented_case = indent(case, ' ' * (prefix_len + 4))
194 return context + '\n' + indented_case
194 return context + '\n' + indented_case
195
195
196 # Gather and run the tests
196 # Gather and run the tests
197
197
198 # yield is allowed in async functions, starting in Python 3.6,
198 # yield is allowed in async functions, starting in Python 3.6,
199 # and yield from is not allowed in any version
199 # and yield from is not allowed in any version
200 vals = ('return', 'yield', 'yield from (_ for _ in range(3))')
200 vals = ('return', 'yield', 'yield from (_ for _ in range(3))')
201 async_safe = (True,
201 async_safe = (True,
202 True,
202 True,
203 False)
203 False)
204 vals = tuple(zip(vals, async_safe))
204 vals = tuple(zip(vals, async_safe))
205
205
206 success_tests = zip(self._get_top_level_cases(), repeat(False))
206 success_tests = zip(self._get_top_level_cases(), repeat(False))
207 failure_tests = zip(self._get_ry_syntax_errors(), repeat(True))
207 failure_tests = zip(self._get_ry_syntax_errors(), repeat(True))
208
208
209 tests = chain(success_tests, failure_tests)
209 tests = chain(success_tests, failure_tests)
210
210
211 for context_name, async_func, context in func_contexts:
211 for context_name, async_func, context in func_contexts:
212 for (test_name, test_case), should_fail in tests:
212 for (test_name, test_case), should_fail in tests:
213 nested_case = nest_case(context, test_case)
213 nested_case = nest_case(context, test_case)
214
214
215 for val, async_safe in vals:
215 for val, async_safe in vals:
216 val_should_fail = (should_fail or
216 val_should_fail = (should_fail or
217 (async_func and not async_safe))
217 (async_func and not async_safe))
218
218
219 test_id = (context_name, test_name, val)
219 test_id = (context_name, test_name, val)
220 cell = nested_case.format(val=val)
220 cell = nested_case.format(val=val)
221
221
222 with self.subTest(test_id):
222 with self.subTest(test_id):
223 if val_should_fail:
223 if val_should_fail:
224 msg = ("SyntaxError not raised for %s" %
224 msg = ("SyntaxError not raised for %s" %
225 str(test_id))
225 str(test_id))
226 with self.assertRaises(SyntaxError, msg=msg):
226 with self.assertRaises(SyntaxError, msg=msg):
227 iprc(cell)
227 iprc(cell)
228
228
229 print(cell)
229 print(cell)
230 else:
230 else:
231 iprc(cell)
231 iprc(cell)
232
232
233 def test_nonlocal(self):
233 def test_nonlocal(self):
234 # fails if outer scope is not a function scope or if var not defined
234 # fails if outer scope is not a function scope or if var not defined
235 with self.assertRaises(SyntaxError):
235 with self.assertRaises(SyntaxError):
236 iprc("nonlocal x")
236 iprc("nonlocal x")
237 iprc("""
237 iprc("""
238 x = 1
238 x = 1
239 def f():
239 def f():
240 nonlocal x
240 nonlocal x
241 x = 10000
241 x = 10000
242 yield x
242 yield x
243 """)
243 """)
244 iprc("""
244 iprc("""
245 def f():
245 def f():
246 def g():
246 def g():
247 nonlocal x
247 nonlocal x
248 x = 10000
248 x = 10000
249 yield x
249 yield x
250 """)
250 """)
251
251
252 # works if outer scope is a function scope and var exists
252 # works if outer scope is a function scope and var exists
253 iprc("""
253 iprc("""
254 def f():
254 def f():
255 x = 20
255 x = 20
256 def g():
256 def g():
257 nonlocal x
257 nonlocal x
258 x = 10000
258 x = 10000
259 yield x
259 yield x
260 """)
260 """)
261
261
262
262
263 def test_execute(self):
263 def test_execute(self):
264 iprc("""
264 iprc("""
265 import asyncio
265 import asyncio
266 await asyncio.sleep(0.001)
266 await asyncio.sleep(0.001)
267 """
267 """
268 )
268 )
269
269
270 def test_autoawait(self):
270 def test_autoawait(self):
271 iprc("%autoawait False")
271 iprc("%autoawait False")
272 iprc("%autoawait True")
272 iprc("%autoawait True")
273 iprc("""
273 iprc("""
274 from asyncio import sleep
274 from asyncio import sleep
275 await sleep(0.1)
275 await sleep(0.1)
276 """
276 """
277 )
277 )
278
278
279 def test_memory_error(self):
279 if sys.version_info < (3,9):
280 with self.assertRaises(MemoryError):
280 # new pgen parser in 3.9 does not raise MemoryError on too many nested
281 iprc("(" * 200 + ")" * 200)
281 # parens anymore
282 def test_memory_error(self):
283 with self.assertRaises(MemoryError):
284 iprc("(" * 200 + ")" * 200)
282
285
283 @skip_without('curio')
286 @skip_without('curio')
284 def test_autoawait_curio(self):
287 def test_autoawait_curio(self):
285 iprc("%autoawait curio")
288 iprc("%autoawait curio")
286
289
287 @skip_without('trio')
290 @skip_without('trio')
288 def test_autoawait_trio(self):
291 def test_autoawait_trio(self):
289 iprc("%autoawait trio")
292 iprc("%autoawait trio")
290
293
291 @skip_without('trio')
294 @skip_without('trio')
292 def test_autoawait_trio_wrong_sleep(self):
295 def test_autoawait_trio_wrong_sleep(self):
293 iprc("%autoawait trio")
296 iprc("%autoawait trio")
294 res = iprc_nr("""
297 res = iprc_nr("""
295 import asyncio
298 import asyncio
296 await asyncio.sleep(0)
299 await asyncio.sleep(0)
297 """)
300 """)
298 with nt.assert_raises(TypeError):
301 with nt.assert_raises(TypeError):
299 res.raise_error()
302 res.raise_error()
300
303
301 @skip_without('trio')
304 @skip_without('trio')
302 def test_autoawait_asyncio_wrong_sleep(self):
305 def test_autoawait_asyncio_wrong_sleep(self):
303 iprc("%autoawait asyncio")
306 iprc("%autoawait asyncio")
304 res = iprc_nr("""
307 res = iprc_nr("""
305 import trio
308 import trio
306 await trio.sleep(0)
309 await trio.sleep(0)
307 """)
310 """)
308 with nt.assert_raises(RuntimeError):
311 with nt.assert_raises(RuntimeError):
309 res.raise_error()
312 res.raise_error()
310
313
311
314
312 def tearDown(self):
315 def tearDown(self):
313 ip.loop_runner = "asyncio"
316 ip.loop_runner = "asyncio"
@@ -1,439 +1,447 b''
1 """Tests for the object inspection functionality.
1 """Tests for the object inspection functionality.
2 """
2 """
3
3
4 # Copyright (c) IPython Development Team.
4 # Copyright (c) IPython Development Team.
5 # Distributed under the terms of the Modified BSD License.
5 # Distributed under the terms of the Modified BSD License.
6
6
7
7
8 from inspect import signature, Signature, Parameter
8 from inspect import signature, Signature, Parameter
9 import os
9 import os
10 import re
10 import re
11
11
12 import nose.tools as nt
12 import nose.tools as nt
13
13
14 from .. import oinspect
14 from .. import oinspect
15
15
16 from decorator import decorator
16 from decorator import decorator
17
17
18 from IPython.testing.tools import AssertPrints, AssertNotPrints
18 from IPython.testing.tools import AssertPrints, AssertNotPrints
19 from IPython.utils.path import compress_user
19 from IPython.utils.path import compress_user
20
20
21
21
22 #-----------------------------------------------------------------------------
22 #-----------------------------------------------------------------------------
23 # Globals and constants
23 # Globals and constants
24 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
25
25
26 inspector = None
26 inspector = None
27
27
28 def setup_module():
28 def setup_module():
29 global inspector
29 global inspector
30 inspector = oinspect.Inspector()
30 inspector = oinspect.Inspector()
31
31
32
32
33 #-----------------------------------------------------------------------------
33 #-----------------------------------------------------------------------------
34 # Local utilities
34 # Local utilities
35 #-----------------------------------------------------------------------------
35 #-----------------------------------------------------------------------------
36
36
37 # WARNING: since this test checks the line number where a function is
37 # WARNING: since this test checks the line number where a function is
38 # defined, if any code is inserted above, the following line will need to be
38 # defined, if any code is inserted above, the following line will need to be
39 # updated. Do NOT insert any whitespace between the next line and the function
39 # updated. Do NOT insert any whitespace between the next line and the function
40 # definition below.
40 # definition below.
41 THIS_LINE_NUMBER = 41 # Put here the actual number of this line
41 THIS_LINE_NUMBER = 41 # Put here the actual number of this line
42
42
43 from unittest import TestCase
43 from unittest import TestCase
44
44
45 class Test(TestCase):
45 class Test(TestCase):
46
46
47 def test_find_source_lines(self):
47 def test_find_source_lines(self):
48 self.assertEqual(oinspect.find_source_lines(Test.test_find_source_lines),
48 self.assertEqual(oinspect.find_source_lines(Test.test_find_source_lines),
49 THIS_LINE_NUMBER+6)
49 THIS_LINE_NUMBER+6)
50
50
51
51
52 # A couple of utilities to ensure these tests work the same from a source or a
52 # A couple of utilities to ensure these tests work the same from a source or a
53 # binary install
53 # binary install
54 def pyfile(fname):
54 def pyfile(fname):
55 return os.path.normcase(re.sub('.py[co]$', '.py', fname))
55 return os.path.normcase(re.sub('.py[co]$', '.py', fname))
56
56
57
57
58 def match_pyfiles(f1, f2):
58 def match_pyfiles(f1, f2):
59 nt.assert_equal(pyfile(f1), pyfile(f2))
59 nt.assert_equal(pyfile(f1), pyfile(f2))
60
60
61
61
62 def test_find_file():
62 def test_find_file():
63 match_pyfiles(oinspect.find_file(test_find_file), os.path.abspath(__file__))
63 match_pyfiles(oinspect.find_file(test_find_file), os.path.abspath(__file__))
64
64
65
65
66 def test_find_file_decorated1():
66 def test_find_file_decorated1():
67
67
68 @decorator
68 @decorator
69 def noop1(f):
69 def noop1(f):
70 def wrapper(*a, **kw):
70 def wrapper(*a, **kw):
71 return f(*a, **kw)
71 return f(*a, **kw)
72 return wrapper
72 return wrapper
73
73
74 @noop1
74 @noop1
75 def f(x):
75 def f(x):
76 "My docstring"
76 "My docstring"
77
77
78 match_pyfiles(oinspect.find_file(f), os.path.abspath(__file__))
78 match_pyfiles(oinspect.find_file(f), os.path.abspath(__file__))
79 nt.assert_equal(f.__doc__, "My docstring")
79 nt.assert_equal(f.__doc__, "My docstring")
80
80
81
81
82 def test_find_file_decorated2():
82 def test_find_file_decorated2():
83
83
84 @decorator
84 @decorator
85 def noop2(f, *a, **kw):
85 def noop2(f, *a, **kw):
86 return f(*a, **kw)
86 return f(*a, **kw)
87
87
88 @noop2
88 @noop2
89 @noop2
89 @noop2
90 @noop2
90 @noop2
91 def f(x):
91 def f(x):
92 "My docstring 2"
92 "My docstring 2"
93
93
94 match_pyfiles(oinspect.find_file(f), os.path.abspath(__file__))
94 match_pyfiles(oinspect.find_file(f), os.path.abspath(__file__))
95 nt.assert_equal(f.__doc__, "My docstring 2")
95 nt.assert_equal(f.__doc__, "My docstring 2")
96
96
97
97
98 def test_find_file_magic():
98 def test_find_file_magic():
99 run = ip.find_line_magic('run')
99 run = ip.find_line_magic('run')
100 nt.assert_not_equal(oinspect.find_file(run), None)
100 nt.assert_not_equal(oinspect.find_file(run), None)
101
101
102
102
103 # A few generic objects we can then inspect in the tests below
103 # A few generic objects we can then inspect in the tests below
104
104
105 class Call(object):
105 class Call(object):
106 """This is the class docstring."""
106 """This is the class docstring."""
107
107
108 def __init__(self, x, y=1):
108 def __init__(self, x, y=1):
109 """This is the constructor docstring."""
109 """This is the constructor docstring."""
110
110
111 def __call__(self, *a, **kw):
111 def __call__(self, *a, **kw):
112 """This is the call docstring."""
112 """This is the call docstring."""
113
113
114 def method(self, x, z=2):
114 def method(self, x, z=2):
115 """Some method's docstring"""
115 """Some method's docstring"""
116
116
117 class HasSignature(object):
117 class HasSignature(object):
118 """This is the class docstring."""
118 """This is the class docstring."""
119 __signature__ = Signature([Parameter('test', Parameter.POSITIONAL_OR_KEYWORD)])
119 __signature__ = Signature([Parameter('test', Parameter.POSITIONAL_OR_KEYWORD)])
120
120
121 def __init__(self, *args):
121 def __init__(self, *args):
122 """This is the init docstring"""
122 """This is the init docstring"""
123
123
124
124
125 class SimpleClass(object):
125 class SimpleClass(object):
126 def method(self, x, z=2):
126 def method(self, x, z=2):
127 """Some method's docstring"""
127 """Some method's docstring"""
128
128
129
129
130 class Awkward(object):
130 class Awkward(object):
131 def __getattr__(self, name):
131 def __getattr__(self, name):
132 raise Exception(name)
132 raise Exception(name)
133
133
134 class NoBoolCall:
134 class NoBoolCall:
135 """
135 """
136 callable with `__bool__` raising should still be inspect-able.
136 callable with `__bool__` raising should still be inspect-able.
137 """
137 """
138
138
139 def __call__(self):
139 def __call__(self):
140 """does nothing"""
140 """does nothing"""
141 pass
141 pass
142
142
143 def __bool__(self):
143 def __bool__(self):
144 """just raise NotImplemented"""
144 """just raise NotImplemented"""
145 raise NotImplementedError('Must be implemented')
145 raise NotImplementedError('Must be implemented')
146
146
147
147
148 class SerialLiar(object):
148 class SerialLiar(object):
149 """Attribute accesses always get another copy of the same class.
149 """Attribute accesses always get another copy of the same class.
150
150
151 unittest.mock.call does something similar, but it's not ideal for testing
151 unittest.mock.call does something similar, but it's not ideal for testing
152 as the failure mode is to eat all your RAM. This gives up after 10k levels.
152 as the failure mode is to eat all your RAM. This gives up after 10k levels.
153 """
153 """
154 def __init__(self, max_fibbing_twig, lies_told=0):
154 def __init__(self, max_fibbing_twig, lies_told=0):
155 if lies_told > 10000:
155 if lies_told > 10000:
156 raise RuntimeError('Nose too long, honesty is the best policy')
156 raise RuntimeError('Nose too long, honesty is the best policy')
157 self.max_fibbing_twig = max_fibbing_twig
157 self.max_fibbing_twig = max_fibbing_twig
158 self.lies_told = lies_told
158 self.lies_told = lies_told
159 max_fibbing_twig[0] = max(max_fibbing_twig[0], lies_told)
159 max_fibbing_twig[0] = max(max_fibbing_twig[0], lies_told)
160
160
161 def __getattr__(self, item):
161 def __getattr__(self, item):
162 return SerialLiar(self.max_fibbing_twig, self.lies_told + 1)
162 return SerialLiar(self.max_fibbing_twig, self.lies_told + 1)
163
163
164 #-----------------------------------------------------------------------------
164 #-----------------------------------------------------------------------------
165 # Tests
165 # Tests
166 #-----------------------------------------------------------------------------
166 #-----------------------------------------------------------------------------
167
167
168 def test_info():
168 def test_info():
169 "Check that Inspector.info fills out various fields as expected."
169 "Check that Inspector.info fills out various fields as expected."
170 i = inspector.info(Call, oname='Call')
170 i = inspector.info(Call, oname='Call')
171 nt.assert_equal(i['type_name'], 'type')
171 nt.assert_equal(i['type_name'], 'type')
172 expted_class = str(type(type)) # <class 'type'> (Python 3) or <type 'type'>
172 expted_class = str(type(type)) # <class 'type'> (Python 3) or <type 'type'>
173 nt.assert_equal(i['base_class'], expted_class)
173 nt.assert_equal(i['base_class'], expted_class)
174 nt.assert_regex(i['string_form'], "<class 'IPython.core.tests.test_oinspect.Call'( at 0x[0-9a-f]{1,9})?>")
174 nt.assert_regex(i['string_form'], "<class 'IPython.core.tests.test_oinspect.Call'( at 0x[0-9a-f]{1,9})?>")
175 fname = __file__
175 fname = __file__
176 if fname.endswith(".pyc"):
176 if fname.endswith(".pyc"):
177 fname = fname[:-1]
177 fname = fname[:-1]
178 # case-insensitive comparison needed on some filesystems
178 # case-insensitive comparison needed on some filesystems
179 # e.g. Windows:
179 # e.g. Windows:
180 nt.assert_equal(i['file'].lower(), compress_user(fname).lower())
180 nt.assert_equal(i['file'].lower(), compress_user(fname).lower())
181 nt.assert_equal(i['definition'], None)
181 nt.assert_equal(i['definition'], None)
182 nt.assert_equal(i['docstring'], Call.__doc__)
182 nt.assert_equal(i['docstring'], Call.__doc__)
183 nt.assert_equal(i['source'], None)
183 nt.assert_equal(i['source'], None)
184 nt.assert_true(i['isclass'])
184 nt.assert_true(i['isclass'])
185 nt.assert_equal(i['init_definition'], "Call(x, y=1)")
185 nt.assert_equal(i['init_definition'], "Call(x, y=1)")
186 nt.assert_equal(i['init_docstring'], Call.__init__.__doc__)
186 nt.assert_equal(i['init_docstring'], Call.__init__.__doc__)
187
187
188 i = inspector.info(Call, detail_level=1)
188 i = inspector.info(Call, detail_level=1)
189 nt.assert_not_equal(i['source'], None)
189 nt.assert_not_equal(i['source'], None)
190 nt.assert_equal(i['docstring'], None)
190 nt.assert_equal(i['docstring'], None)
191
191
192 c = Call(1)
192 c = Call(1)
193 c.__doc__ = "Modified instance docstring"
193 c.__doc__ = "Modified instance docstring"
194 i = inspector.info(c)
194 i = inspector.info(c)
195 nt.assert_equal(i['type_name'], 'Call')
195 nt.assert_equal(i['type_name'], 'Call')
196 nt.assert_equal(i['docstring'], "Modified instance docstring")
196 nt.assert_equal(i['docstring'], "Modified instance docstring")
197 nt.assert_equal(i['class_docstring'], Call.__doc__)
197 nt.assert_equal(i['class_docstring'], Call.__doc__)
198 nt.assert_equal(i['init_docstring'], Call.__init__.__doc__)
198 nt.assert_equal(i['init_docstring'], Call.__init__.__doc__)
199 nt.assert_equal(i['call_docstring'], Call.__call__.__doc__)
199 nt.assert_equal(i['call_docstring'], Call.__call__.__doc__)
200
200
201 def test_class_signature():
201 def test_class_signature():
202 info = inspector.info(HasSignature, 'HasSignature')
202 info = inspector.info(HasSignature, 'HasSignature')
203 nt.assert_equal(info['init_definition'], "HasSignature(test)")
203 nt.assert_equal(info['init_definition'], "HasSignature(test)")
204 nt.assert_equal(info['init_docstring'], HasSignature.__init__.__doc__)
204 nt.assert_equal(info['init_docstring'], HasSignature.__init__.__doc__)
205
205
206 def test_info_awkward():
206 def test_info_awkward():
207 # Just test that this doesn't throw an error.
207 # Just test that this doesn't throw an error.
208 inspector.info(Awkward())
208 inspector.info(Awkward())
209
209
210 def test_bool_raise():
210 def test_bool_raise():
211 inspector.info(NoBoolCall())
211 inspector.info(NoBoolCall())
212
212
213 def test_info_serialliar():
213 def test_info_serialliar():
214 fib_tracker = [0]
214 fib_tracker = [0]
215 inspector.info(SerialLiar(fib_tracker))
215 inspector.info(SerialLiar(fib_tracker))
216
216
217 # Nested attribute access should be cut off at 100 levels deep to avoid
217 # Nested attribute access should be cut off at 100 levels deep to avoid
218 # infinite loops: https://github.com/ipython/ipython/issues/9122
218 # infinite loops: https://github.com/ipython/ipython/issues/9122
219 nt.assert_less(fib_tracker[0], 9000)
219 nt.assert_less(fib_tracker[0], 9000)
220
220
221 def support_function_one(x, y=2, *a, **kw):
221 def support_function_one(x, y=2, *a, **kw):
222 """A simple function."""
222 """A simple function."""
223
223
224 def test_calldef_none():
224 def test_calldef_none():
225 # We should ignore __call__ for all of these.
225 # We should ignore __call__ for all of these.
226 for obj in [support_function_one, SimpleClass().method, any, str.upper]:
226 for obj in [support_function_one, SimpleClass().method, any, str.upper]:
227 i = inspector.info(obj)
227 i = inspector.info(obj)
228 nt.assert_is(i['call_def'], None)
228 nt.assert_is(i['call_def'], None)
229
229
230 def f_kwarg(pos, *, kwonly):
230 def f_kwarg(pos, *, kwonly):
231 pass
231 pass
232
232
233 def test_definition_kwonlyargs():
233 def test_definition_kwonlyargs():
234 i = inspector.info(f_kwarg, oname='f_kwarg') # analysis:ignore
234 i = inspector.info(f_kwarg, oname='f_kwarg') # analysis:ignore
235 nt.assert_equal(i['definition'], "f_kwarg(pos, *, kwonly)")
235 nt.assert_equal(i['definition'], "f_kwarg(pos, *, kwonly)")
236
236
237 def test_getdoc():
237 def test_getdoc():
238 class A(object):
238 class A(object):
239 """standard docstring"""
239 """standard docstring"""
240 pass
240 pass
241
241
242 class B(object):
242 class B(object):
243 """standard docstring"""
243 """standard docstring"""
244 def getdoc(self):
244 def getdoc(self):
245 return "custom docstring"
245 return "custom docstring"
246
246
247 class C(object):
247 class C(object):
248 """standard docstring"""
248 """standard docstring"""
249 def getdoc(self):
249 def getdoc(self):
250 return None
250 return None
251
251
252 a = A()
252 a = A()
253 b = B()
253 b = B()
254 c = C()
254 c = C()
255
255
256 nt.assert_equal(oinspect.getdoc(a), "standard docstring")
256 nt.assert_equal(oinspect.getdoc(a), "standard docstring")
257 nt.assert_equal(oinspect.getdoc(b), "custom docstring")
257 nt.assert_equal(oinspect.getdoc(b), "custom docstring")
258 nt.assert_equal(oinspect.getdoc(c), "standard docstring")
258 nt.assert_equal(oinspect.getdoc(c), "standard docstring")
259
259
260
260
261 def test_empty_property_has_no_source():
261 def test_empty_property_has_no_source():
262 i = inspector.info(property(), detail_level=1)
262 i = inspector.info(property(), detail_level=1)
263 nt.assert_is(i['source'], None)
263 nt.assert_is(i['source'], None)
264
264
265
265
266 def test_property_sources():
266 def test_property_sources():
267 import posixpath
267 import posixpath
268 # A simple adder whose source and signature stays
268 # A simple adder whose source and signature stays
269 # the same across Python distributions
269 # the same across Python distributions
270 def simple_add(a, b):
270 def simple_add(a, b):
271 "Adds two numbers"
271 "Adds two numbers"
272 return a + b
272 return a + b
273
273
274 class A(object):
274 class A(object):
275 @property
275 @property
276 def foo(self):
276 def foo(self):
277 return 'bar'
277 return 'bar'
278
278
279 foo = foo.setter(lambda self, v: setattr(self, 'bar', v))
279 foo = foo.setter(lambda self, v: setattr(self, 'bar', v))
280
280
281 dname = property(posixpath.dirname)
281 dname = property(posixpath.dirname)
282 adder = property(simple_add)
282 adder = property(simple_add)
283
283
284 i = inspector.info(A.foo, detail_level=1)
284 i = inspector.info(A.foo, detail_level=1)
285 nt.assert_in('def foo(self):', i['source'])
285 nt.assert_in('def foo(self):', i['source'])
286 nt.assert_in('lambda self, v:', i['source'])
286 nt.assert_in('lambda self, v:', i['source'])
287
287
288 i = inspector.info(A.dname, detail_level=1)
288 i = inspector.info(A.dname, detail_level=1)
289 nt.assert_in('def dirname(p)', i['source'])
289 nt.assert_in('def dirname(p)', i['source'])
290
290
291 i = inspector.info(A.adder, detail_level=1)
291 i = inspector.info(A.adder, detail_level=1)
292 nt.assert_in('def simple_add(a, b)', i['source'])
292 nt.assert_in('def simple_add(a, b)', i['source'])
293
293
294
294
295 def test_property_docstring_is_in_info_for_detail_level_0():
295 def test_property_docstring_is_in_info_for_detail_level_0():
296 class A(object):
296 class A(object):
297 @property
297 @property
298 def foobar(self):
298 def foobar(self):
299 """This is `foobar` property."""
299 """This is `foobar` property."""
300 pass
300 pass
301
301
302 ip.user_ns['a_obj'] = A()
302 ip.user_ns['a_obj'] = A()
303 nt.assert_equal(
303 nt.assert_equal(
304 'This is `foobar` property.',
304 'This is `foobar` property.',
305 ip.object_inspect('a_obj.foobar', detail_level=0)['docstring'])
305 ip.object_inspect('a_obj.foobar', detail_level=0)['docstring'])
306
306
307 ip.user_ns['a_cls'] = A
307 ip.user_ns['a_cls'] = A
308 nt.assert_equal(
308 nt.assert_equal(
309 'This is `foobar` property.',
309 'This is `foobar` property.',
310 ip.object_inspect('a_cls.foobar', detail_level=0)['docstring'])
310 ip.object_inspect('a_cls.foobar', detail_level=0)['docstring'])
311
311
312
312
313 def test_pdef():
313 def test_pdef():
314 # See gh-1914
314 # See gh-1914
315 def foo(): pass
315 def foo(): pass
316 inspector.pdef(foo, 'foo')
316 inspector.pdef(foo, 'foo')
317
317
318
318
319 def test_pinfo_nonascii():
319 def test_pinfo_nonascii():
320 # See gh-1177
320 # See gh-1177
321 from . import nonascii2
321 from . import nonascii2
322 ip.user_ns['nonascii2'] = nonascii2
322 ip.user_ns['nonascii2'] = nonascii2
323 ip._inspect('pinfo', 'nonascii2', detail_level=1)
323 ip._inspect('pinfo', 'nonascii2', detail_level=1)
324
324
325 def test_pinfo_type():
325 def test_pinfo_type():
326 """
326 """
327 type can fail in various edge case, for example `type.__subclass__()`
327 type can fail in various edge case, for example `type.__subclass__()`
328 """
328 """
329 ip._inspect('pinfo', 'type')
329 ip._inspect('pinfo', 'type')
330
330
331
331
332 def test_pinfo_docstring_no_source():
332 def test_pinfo_docstring_no_source():
333 """Docstring should be included with detail_level=1 if there is no source"""
333 """Docstring should be included with detail_level=1 if there is no source"""
334 with AssertPrints('Docstring:'):
334 with AssertPrints('Docstring:'):
335 ip._inspect('pinfo', 'str.format', detail_level=0)
335 ip._inspect('pinfo', 'str.format', detail_level=0)
336 with AssertPrints('Docstring:'):
336 with AssertPrints('Docstring:'):
337 ip._inspect('pinfo', 'str.format', detail_level=1)
337 ip._inspect('pinfo', 'str.format', detail_level=1)
338
338
339
339
340 def test_pinfo_no_docstring_if_source():
340 def test_pinfo_no_docstring_if_source():
341 """Docstring should not be included with detail_level=1 if source is found"""
341 """Docstring should not be included with detail_level=1 if source is found"""
342 def foo():
342 def foo():
343 """foo has a docstring"""
343 """foo has a docstring"""
344
344
345 ip.user_ns['foo'] = foo
345 ip.user_ns['foo'] = foo
346
346
347 with AssertPrints('Docstring:'):
347 with AssertPrints('Docstring:'):
348 ip._inspect('pinfo', 'foo', detail_level=0)
348 ip._inspect('pinfo', 'foo', detail_level=0)
349 with AssertPrints('Source:'):
349 with AssertPrints('Source:'):
350 ip._inspect('pinfo', 'foo', detail_level=1)
350 ip._inspect('pinfo', 'foo', detail_level=1)
351 with AssertNotPrints('Docstring:'):
351 with AssertNotPrints('Docstring:'):
352 ip._inspect('pinfo', 'foo', detail_level=1)
352 ip._inspect('pinfo', 'foo', detail_level=1)
353
353
354
354
355 def test_pinfo_docstring_if_detail_and_no_source():
355 def test_pinfo_docstring_if_detail_and_no_source():
356 """ Docstring should be displayed if source info not available """
356 """ Docstring should be displayed if source info not available """
357 obj_def = '''class Foo(object):
357 obj_def = '''class Foo(object):
358 """ This is a docstring for Foo """
358 """ This is a docstring for Foo """
359 def bar(self):
359 def bar(self):
360 """ This is a docstring for Foo.bar """
360 """ This is a docstring for Foo.bar """
361 pass
361 pass
362 '''
362 '''
363
363
364 ip.run_cell(obj_def)
364 ip.run_cell(obj_def)
365 ip.run_cell('foo = Foo()')
365 ip.run_cell('foo = Foo()')
366
366
367 with AssertNotPrints("Source:"):
367 with AssertNotPrints("Source:"):
368 with AssertPrints('Docstring:'):
368 with AssertPrints('Docstring:'):
369 ip._inspect('pinfo', 'foo', detail_level=0)
369 ip._inspect('pinfo', 'foo', detail_level=0)
370 with AssertPrints('Docstring:'):
370 with AssertPrints('Docstring:'):
371 ip._inspect('pinfo', 'foo', detail_level=1)
371 ip._inspect('pinfo', 'foo', detail_level=1)
372 with AssertPrints('Docstring:'):
372 with AssertPrints('Docstring:'):
373 ip._inspect('pinfo', 'foo.bar', detail_level=0)
373 ip._inspect('pinfo', 'foo.bar', detail_level=0)
374
374
375 with AssertNotPrints('Docstring:'):
375 with AssertNotPrints('Docstring:'):
376 with AssertPrints('Source:'):
376 with AssertPrints('Source:'):
377 ip._inspect('pinfo', 'foo.bar', detail_level=1)
377 ip._inspect('pinfo', 'foo.bar', detail_level=1)
378
378
379
379
380 def test_pinfo_magic():
380 def test_pinfo_magic():
381 with AssertPrints('Docstring:'):
381 with AssertPrints('Docstring:'):
382 ip._inspect('pinfo', 'lsmagic', detail_level=0)
382 ip._inspect('pinfo', 'lsmagic', detail_level=0)
383
383
384 with AssertPrints('Source:'):
384 with AssertPrints('Source:'):
385 ip._inspect('pinfo', 'lsmagic', detail_level=1)
385 ip._inspect('pinfo', 'lsmagic', detail_level=1)
386
386
387
387
388 def test_init_colors():
388 def test_init_colors():
389 # ensure colors are not present in signature info
389 # ensure colors are not present in signature info
390 info = inspector.info(HasSignature)
390 info = inspector.info(HasSignature)
391 init_def = info['init_definition']
391 init_def = info['init_definition']
392 nt.assert_not_in('[0m', init_def)
392 nt.assert_not_in('[0m', init_def)
393
393
394
394
395 def test_builtin_init():
395 def test_builtin_init():
396 info = inspector.info(list)
396 info = inspector.info(list)
397 init_def = info['init_definition']
397 init_def = info['init_definition']
398 nt.assert_is_not_none(init_def)
398 nt.assert_is_not_none(init_def)
399
399
400
400
401 def test_render_signature_short():
401 def test_render_signature_short():
402 def short_fun(a=1): pass
402 def short_fun(a=1): pass
403 sig = oinspect._render_signature(
403 sig = oinspect._render_signature(
404 signature(short_fun),
404 signature(short_fun),
405 short_fun.__name__,
405 short_fun.__name__,
406 )
406 )
407 nt.assert_equal(sig, 'short_fun(a=1)')
407 nt.assert_equal(sig, 'short_fun(a=1)')
408
408
409
409
410 def test_render_signature_long():
410 def test_render_signature_long():
411 from typing import Optional
411 from typing import Optional
412
412
413 def long_function(
413 def long_function(
414 a_really_long_parameter: int,
414 a_really_long_parameter: int,
415 and_another_long_one: bool = False,
415 and_another_long_one: bool = False,
416 let_us_make_sure_this_is_looong: Optional[str] = None,
416 let_us_make_sure_this_is_looong: Optional[str] = None,
417 ) -> bool: pass
417 ) -> bool: pass
418
418
419 sig = oinspect._render_signature(
419 sig = oinspect._render_signature(
420 signature(long_function),
420 signature(long_function),
421 long_function.__name__,
421 long_function.__name__,
422 )
422 )
423 nt.assert_in(sig, [
423 nt.assert_in(sig, [
424 # Python >=3.9
425 '''\
426 long_function(
427 a_really_long_parameter: int,
428 and_another_long_one: bool = False,
429 let_us_make_sure_this_is_looong: Optional[str] = None,
430 ) -> bool\
431 ''',
424 # Python >=3.7
432 # Python >=3.7
425 '''\
433 '''\
426 long_function(
434 long_function(
427 a_really_long_parameter: int,
435 a_really_long_parameter: int,
428 and_another_long_one: bool = False,
436 and_another_long_one: bool = False,
429 let_us_make_sure_this_is_looong: Union[str, NoneType] = None,
437 let_us_make_sure_this_is_looong: Union[str, NoneType] = None,
430 ) -> bool\
438 ) -> bool\
431 ''', # Python <=3.6
439 ''', # Python <=3.6
432 '''\
440 '''\
433 long_function(
441 long_function(
434 a_really_long_parameter:int,
442 a_really_long_parameter:int,
435 and_another_long_one:bool=False,
443 and_another_long_one:bool=False,
436 let_us_make_sure_this_is_looong:Union[str, NoneType]=None,
444 let_us_make_sure_this_is_looong:Union[str, NoneType]=None,
437 ) -> bool\
445 ) -> bool\
438 ''',
446 ''',
439 ])
447 ])
@@ -1,408 +1,412 b''
1 # encoding: utf-8
1 # encoding: utf-8
2 """Tests for IPython.core.ultratb
2 """Tests for IPython.core.ultratb
3 """
3 """
4 import io
4 import io
5 import logging
5 import logging
6 import re
6 import re
7 import sys
7 import sys
8 import os.path
8 import os.path
9 from textwrap import dedent
9 from textwrap import dedent
10 import traceback
10 import traceback
11 import unittest
11 import unittest
12
12
13 from IPython.core.ultratb import ColorTB, VerboseTB
13 from IPython.core.ultratb import ColorTB, VerboseTB
14
14
15
15
16 from IPython.testing import tools as tt
16 from IPython.testing import tools as tt
17 from IPython.testing.decorators import onlyif_unicode_paths
17 from IPython.testing.decorators import onlyif_unicode_paths
18 from IPython.utils.syspathcontext import prepended_to_syspath
18 from IPython.utils.syspathcontext import prepended_to_syspath
19 from IPython.utils.tempdir import TemporaryDirectory
19 from IPython.utils.tempdir import TemporaryDirectory
20
20
21 file_1 = """1
21 file_1 = """1
22 2
22 2
23 3
23 3
24 def f():
24 def f():
25 1/0
25 1/0
26 """
26 """
27
27
28 file_2 = """def f():
28 file_2 = """def f():
29 1/0
29 1/0
30 """
30 """
31
31
32
32
33 def recursionlimit(frames):
33 def recursionlimit(frames):
34 """
34 """
35 decorator to set the recursion limit temporarily
35 decorator to set the recursion limit temporarily
36 """
36 """
37
37
38 def inner(test_function):
38 def inner(test_function):
39 def wrapper(*args, **kwargs):
39 def wrapper(*args, **kwargs):
40 rl = sys.getrecursionlimit()
40 rl = sys.getrecursionlimit()
41 sys.setrecursionlimit(frames)
41 sys.setrecursionlimit(frames)
42 try:
42 try:
43 return test_function(*args, **kwargs)
43 return test_function(*args, **kwargs)
44 finally:
44 finally:
45 sys.setrecursionlimit(rl)
45 sys.setrecursionlimit(rl)
46
46
47 return wrapper
47 return wrapper
48
48
49 return inner
49 return inner
50
50
51
51
52 class ChangedPyFileTest(unittest.TestCase):
52 class ChangedPyFileTest(unittest.TestCase):
53 def test_changing_py_file(self):
53 def test_changing_py_file(self):
54 """Traceback produced if the line where the error occurred is missing?
54 """Traceback produced if the line where the error occurred is missing?
55
55
56 https://github.com/ipython/ipython/issues/1456
56 https://github.com/ipython/ipython/issues/1456
57 """
57 """
58 with TemporaryDirectory() as td:
58 with TemporaryDirectory() as td:
59 fname = os.path.join(td, "foo.py")
59 fname = os.path.join(td, "foo.py")
60 with open(fname, "w") as f:
60 with open(fname, "w") as f:
61 f.write(file_1)
61 f.write(file_1)
62
62
63 with prepended_to_syspath(td):
63 with prepended_to_syspath(td):
64 ip.run_cell("import foo")
64 ip.run_cell("import foo")
65
65
66 with tt.AssertPrints("ZeroDivisionError"):
66 with tt.AssertPrints("ZeroDivisionError"):
67 ip.run_cell("foo.f()")
67 ip.run_cell("foo.f()")
68
68
69 # Make the file shorter, so the line of the error is missing.
69 # Make the file shorter, so the line of the error is missing.
70 with open(fname, "w") as f:
70 with open(fname, "w") as f:
71 f.write(file_2)
71 f.write(file_2)
72
72
73 # For some reason, this was failing on the *second* call after
73 # For some reason, this was failing on the *second* call after
74 # changing the file, so we call f() twice.
74 # changing the file, so we call f() twice.
75 with tt.AssertNotPrints("Internal Python error", channel='stderr'):
75 with tt.AssertNotPrints("Internal Python error", channel='stderr'):
76 with tt.AssertPrints("ZeroDivisionError"):
76 with tt.AssertPrints("ZeroDivisionError"):
77 ip.run_cell("foo.f()")
77 ip.run_cell("foo.f()")
78 with tt.AssertPrints("ZeroDivisionError"):
78 with tt.AssertPrints("ZeroDivisionError"):
79 ip.run_cell("foo.f()")
79 ip.run_cell("foo.f()")
80
80
81 iso_8859_5_file = u'''# coding: iso-8859-5
81 iso_8859_5_file = u'''# coding: iso-8859-5
82
82
83 def fail():
83 def fail():
84 """Π΄Π±Π˜Π–"""
84 """Π΄Π±Π˜Π–"""
85 1/0 # Π΄Π±Π˜Π–
85 1/0 # Π΄Π±Π˜Π–
86 '''
86 '''
87
87
88 class NonAsciiTest(unittest.TestCase):
88 class NonAsciiTest(unittest.TestCase):
89 @onlyif_unicode_paths
89 @onlyif_unicode_paths
90 def test_nonascii_path(self):
90 def test_nonascii_path(self):
91 # Non-ascii directory name as well.
91 # Non-ascii directory name as well.
92 with TemporaryDirectory(suffix=u'Γ©') as td:
92 with TemporaryDirectory(suffix=u'Γ©') as td:
93 fname = os.path.join(td, u"fooΓ©.py")
93 fname = os.path.join(td, u"fooΓ©.py")
94 with open(fname, "w") as f:
94 with open(fname, "w") as f:
95 f.write(file_1)
95 f.write(file_1)
96
96
97 with prepended_to_syspath(td):
97 with prepended_to_syspath(td):
98 ip.run_cell("import foo")
98 ip.run_cell("import foo")
99
99
100 with tt.AssertPrints("ZeroDivisionError"):
100 with tt.AssertPrints("ZeroDivisionError"):
101 ip.run_cell("foo.f()")
101 ip.run_cell("foo.f()")
102
102
103 def test_iso8859_5(self):
103 def test_iso8859_5(self):
104 with TemporaryDirectory() as td:
104 with TemporaryDirectory() as td:
105 fname = os.path.join(td, 'dfghjkl.py')
105 fname = os.path.join(td, 'dfghjkl.py')
106
106
107 with io.open(fname, 'w', encoding='iso-8859-5') as f:
107 with io.open(fname, 'w', encoding='iso-8859-5') as f:
108 f.write(iso_8859_5_file)
108 f.write(iso_8859_5_file)
109
109
110 with prepended_to_syspath(td):
110 with prepended_to_syspath(td):
111 ip.run_cell("from dfghjkl import fail")
111 ip.run_cell("from dfghjkl import fail")
112
112
113 with tt.AssertPrints("ZeroDivisionError"):
113 with tt.AssertPrints("ZeroDivisionError"):
114 with tt.AssertPrints(u'Π΄Π±Π˜Π–', suppress=False):
114 with tt.AssertPrints(u'Π΄Π±Π˜Π–', suppress=False):
115 ip.run_cell('fail()')
115 ip.run_cell('fail()')
116
116
117 def test_nonascii_msg(self):
117 def test_nonascii_msg(self):
118 cell = u"raise Exception('Γ©')"
118 cell = u"raise Exception('Γ©')"
119 expected = u"Exception('Γ©')"
119 expected = u"Exception('Γ©')"
120 ip.run_cell("%xmode plain")
120 ip.run_cell("%xmode plain")
121 with tt.AssertPrints(expected):
121 with tt.AssertPrints(expected):
122 ip.run_cell(cell)
122 ip.run_cell(cell)
123
123
124 ip.run_cell("%xmode verbose")
124 ip.run_cell("%xmode verbose")
125 with tt.AssertPrints(expected):
125 with tt.AssertPrints(expected):
126 ip.run_cell(cell)
126 ip.run_cell(cell)
127
127
128 ip.run_cell("%xmode context")
128 ip.run_cell("%xmode context")
129 with tt.AssertPrints(expected):
129 with tt.AssertPrints(expected):
130 ip.run_cell(cell)
130 ip.run_cell(cell)
131
131
132 ip.run_cell("%xmode minimal")
132 ip.run_cell("%xmode minimal")
133 with tt.AssertPrints(u"Exception: Γ©"):
133 with tt.AssertPrints(u"Exception: Γ©"):
134 ip.run_cell(cell)
134 ip.run_cell(cell)
135
135
136 # Put this back into Context mode for later tests.
136 # Put this back into Context mode for later tests.
137 ip.run_cell("%xmode context")
137 ip.run_cell("%xmode context")
138
138
139 class NestedGenExprTestCase(unittest.TestCase):
139 class NestedGenExprTestCase(unittest.TestCase):
140 """
140 """
141 Regression test for the following issues:
141 Regression test for the following issues:
142 https://github.com/ipython/ipython/issues/8293
142 https://github.com/ipython/ipython/issues/8293
143 https://github.com/ipython/ipython/issues/8205
143 https://github.com/ipython/ipython/issues/8205
144 """
144 """
145 def test_nested_genexpr(self):
145 def test_nested_genexpr(self):
146 code = dedent(
146 code = dedent(
147 """\
147 """\
148 class SpecificException(Exception):
148 class SpecificException(Exception):
149 pass
149 pass
150
150
151 def foo(x):
151 def foo(x):
152 raise SpecificException("Success!")
152 raise SpecificException("Success!")
153
153
154 sum(sum(foo(x) for _ in [0]) for x in [0])
154 sum(sum(foo(x) for _ in [0]) for x in [0])
155 """
155 """
156 )
156 )
157 with tt.AssertPrints('SpecificException: Success!', suppress=False):
157 with tt.AssertPrints('SpecificException: Success!', suppress=False):
158 ip.run_cell(code)
158 ip.run_cell(code)
159
159
160
160
161 indentationerror_file = """if True:
161 indentationerror_file = """if True:
162 zoon()
162 zoon()
163 """
163 """
164
164
165 class IndentationErrorTest(unittest.TestCase):
165 class IndentationErrorTest(unittest.TestCase):
166 def test_indentationerror_shows_line(self):
166 def test_indentationerror_shows_line(self):
167 # See issue gh-2398
167 # See issue gh-2398
168 with tt.AssertPrints("IndentationError"):
168 with tt.AssertPrints("IndentationError"):
169 with tt.AssertPrints("zoon()", suppress=False):
169 with tt.AssertPrints("zoon()", suppress=False):
170 ip.run_cell(indentationerror_file)
170 ip.run_cell(indentationerror_file)
171
171
172 with TemporaryDirectory() as td:
172 with TemporaryDirectory() as td:
173 fname = os.path.join(td, "foo.py")
173 fname = os.path.join(td, "foo.py")
174 with open(fname, "w") as f:
174 with open(fname, "w") as f:
175 f.write(indentationerror_file)
175 f.write(indentationerror_file)
176
176
177 with tt.AssertPrints("IndentationError"):
177 with tt.AssertPrints("IndentationError"):
178 with tt.AssertPrints("zoon()", suppress=False):
178 with tt.AssertPrints("zoon()", suppress=False):
179 ip.magic('run %s' % fname)
179 ip.magic('run %s' % fname)
180
180
181 se_file_1 = """1
181 se_file_1 = """1
182 2
182 2
183 7/
183 7/
184 """
184 """
185
185
186 se_file_2 = """7/
186 se_file_2 = """7/
187 """
187 """
188
188
189 class SyntaxErrorTest(unittest.TestCase):
189 class SyntaxErrorTest(unittest.TestCase):
190 def test_syntaxerror_without_lineno(self):
190 def test_syntaxerror_without_lineno(self):
191 with tt.AssertNotPrints("TypeError"):
191 with tt.AssertNotPrints("TypeError"):
192 with tt.AssertPrints("line unknown"):
192 with tt.AssertPrints("line unknown"):
193 ip.run_cell("raise SyntaxError()")
193 ip.run_cell("raise SyntaxError()")
194
194
195 def test_syntaxerror_no_stacktrace_at_compile_time(self):
195 def test_syntaxerror_no_stacktrace_at_compile_time(self):
196 syntax_error_at_compile_time = """
196 syntax_error_at_compile_time = """
197 def foo():
197 def foo():
198 ..
198 ..
199 """
199 """
200 with tt.AssertPrints("SyntaxError"):
200 with tt.AssertPrints("SyntaxError"):
201 ip.run_cell(syntax_error_at_compile_time)
201 ip.run_cell(syntax_error_at_compile_time)
202
202
203 with tt.AssertNotPrints("foo()"):
203 with tt.AssertNotPrints("foo()"):
204 ip.run_cell(syntax_error_at_compile_time)
204 ip.run_cell(syntax_error_at_compile_time)
205
205
206 def test_syntaxerror_stacktrace_when_running_compiled_code(self):
206 def test_syntaxerror_stacktrace_when_running_compiled_code(self):
207 syntax_error_at_runtime = """
207 syntax_error_at_runtime = """
208 def foo():
208 def foo():
209 eval("..")
209 eval("..")
210
210
211 def bar():
211 def bar():
212 foo()
212 foo()
213
213
214 bar()
214 bar()
215 """
215 """
216 with tt.AssertPrints("SyntaxError"):
216 with tt.AssertPrints("SyntaxError"):
217 ip.run_cell(syntax_error_at_runtime)
217 ip.run_cell(syntax_error_at_runtime)
218 # Assert syntax error during runtime generate stacktrace
218 # Assert syntax error during runtime generate stacktrace
219 with tt.AssertPrints(["foo()", "bar()"]):
219 with tt.AssertPrints(["foo()", "bar()"]):
220 ip.run_cell(syntax_error_at_runtime)
220 ip.run_cell(syntax_error_at_runtime)
221 del ip.user_ns['bar']
221 del ip.user_ns['bar']
222 del ip.user_ns['foo']
222 del ip.user_ns['foo']
223
223
224 def test_changing_py_file(self):
224 def test_changing_py_file(self):
225 with TemporaryDirectory() as td:
225 with TemporaryDirectory() as td:
226 fname = os.path.join(td, "foo.py")
226 fname = os.path.join(td, "foo.py")
227 with open(fname, 'w') as f:
227 with open(fname, 'w') as f:
228 f.write(se_file_1)
228 f.write(se_file_1)
229
229
230 with tt.AssertPrints(["7/", "SyntaxError"]):
230 with tt.AssertPrints(["7/", "SyntaxError"]):
231 ip.magic("run " + fname)
231 ip.magic("run " + fname)
232
232
233 # Modify the file
233 # Modify the file
234 with open(fname, 'w') as f:
234 with open(fname, 'w') as f:
235 f.write(se_file_2)
235 f.write(se_file_2)
236
236
237 # The SyntaxError should point to the correct line
237 # The SyntaxError should point to the correct line
238 with tt.AssertPrints(["7/", "SyntaxError"]):
238 with tt.AssertPrints(["7/", "SyntaxError"]):
239 ip.magic("run " + fname)
239 ip.magic("run " + fname)
240
240
241 def test_non_syntaxerror(self):
241 def test_non_syntaxerror(self):
242 # SyntaxTB may be called with an error other than a SyntaxError
242 # SyntaxTB may be called with an error other than a SyntaxError
243 # See e.g. gh-4361
243 # See e.g. gh-4361
244 try:
244 try:
245 raise ValueError('QWERTY')
245 raise ValueError('QWERTY')
246 except ValueError:
246 except ValueError:
247 with tt.AssertPrints('QWERTY'):
247 with tt.AssertPrints('QWERTY'):
248 ip.showsyntaxerror()
248 ip.showsyntaxerror()
249
249
250
250 import sys
251 class MemoryErrorTest(unittest.TestCase):
251 if sys.version_info < (3,9):
252 def test_memoryerror(self):
252 """
253 memoryerror_code = "(" * 200 + ")" * 200
253 New 3.9 Pgen Parser does not raise Memory error, except on failed malloc.
254 with tt.AssertPrints("MemoryError"):
254 """
255 ip.run_cell(memoryerror_code)
255 class MemoryErrorTest(unittest.TestCase):
256 def test_memoryerror(self):
257 memoryerror_code = "(" * 200 + ")" * 200
258 with tt.AssertPrints("MemoryError"):
259 ip.run_cell(memoryerror_code)
256
260
257
261
258 class Python3ChainedExceptionsTest(unittest.TestCase):
262 class Python3ChainedExceptionsTest(unittest.TestCase):
259 DIRECT_CAUSE_ERROR_CODE = """
263 DIRECT_CAUSE_ERROR_CODE = """
260 try:
264 try:
261 x = 1 + 2
265 x = 1 + 2
262 print(not_defined_here)
266 print(not_defined_here)
263 except Exception as e:
267 except Exception as e:
264 x += 55
268 x += 55
265 x - 1
269 x - 1
266 y = {}
270 y = {}
267 raise KeyError('uh') from e
271 raise KeyError('uh') from e
268 """
272 """
269
273
270 EXCEPTION_DURING_HANDLING_CODE = """
274 EXCEPTION_DURING_HANDLING_CODE = """
271 try:
275 try:
272 x = 1 + 2
276 x = 1 + 2
273 print(not_defined_here)
277 print(not_defined_here)
274 except Exception as e:
278 except Exception as e:
275 x += 55
279 x += 55
276 x - 1
280 x - 1
277 y = {}
281 y = {}
278 raise KeyError('uh')
282 raise KeyError('uh')
279 """
283 """
280
284
281 SUPPRESS_CHAINING_CODE = """
285 SUPPRESS_CHAINING_CODE = """
282 try:
286 try:
283 1/0
287 1/0
284 except Exception:
288 except Exception:
285 raise ValueError("Yikes") from None
289 raise ValueError("Yikes") from None
286 """
290 """
287
291
288 def test_direct_cause_error(self):
292 def test_direct_cause_error(self):
289 with tt.AssertPrints(["KeyError", "NameError", "direct cause"]):
293 with tt.AssertPrints(["KeyError", "NameError", "direct cause"]):
290 ip.run_cell(self.DIRECT_CAUSE_ERROR_CODE)
294 ip.run_cell(self.DIRECT_CAUSE_ERROR_CODE)
291
295
292 def test_exception_during_handling_error(self):
296 def test_exception_during_handling_error(self):
293 with tt.AssertPrints(["KeyError", "NameError", "During handling"]):
297 with tt.AssertPrints(["KeyError", "NameError", "During handling"]):
294 ip.run_cell(self.EXCEPTION_DURING_HANDLING_CODE)
298 ip.run_cell(self.EXCEPTION_DURING_HANDLING_CODE)
295
299
296 def test_suppress_exception_chaining(self):
300 def test_suppress_exception_chaining(self):
297 with tt.AssertNotPrints("ZeroDivisionError"), \
301 with tt.AssertNotPrints("ZeroDivisionError"), \
298 tt.AssertPrints("ValueError", suppress=False):
302 tt.AssertPrints("ValueError", suppress=False):
299 ip.run_cell(self.SUPPRESS_CHAINING_CODE)
303 ip.run_cell(self.SUPPRESS_CHAINING_CODE)
300
304
301 def test_plain_direct_cause_error(self):
305 def test_plain_direct_cause_error(self):
302 with tt.AssertPrints(["KeyError", "NameError", "direct cause"]):
306 with tt.AssertPrints(["KeyError", "NameError", "direct cause"]):
303 ip.run_cell("%xmode Plain")
307 ip.run_cell("%xmode Plain")
304 ip.run_cell(self.DIRECT_CAUSE_ERROR_CODE)
308 ip.run_cell(self.DIRECT_CAUSE_ERROR_CODE)
305 ip.run_cell("%xmode Verbose")
309 ip.run_cell("%xmode Verbose")
306
310
307 def test_plain_exception_during_handling_error(self):
311 def test_plain_exception_during_handling_error(self):
308 with tt.AssertPrints(["KeyError", "NameError", "During handling"]):
312 with tt.AssertPrints(["KeyError", "NameError", "During handling"]):
309 ip.run_cell("%xmode Plain")
313 ip.run_cell("%xmode Plain")
310 ip.run_cell(self.EXCEPTION_DURING_HANDLING_CODE)
314 ip.run_cell(self.EXCEPTION_DURING_HANDLING_CODE)
311 ip.run_cell("%xmode Verbose")
315 ip.run_cell("%xmode Verbose")
312
316
313 def test_plain_suppress_exception_chaining(self):
317 def test_plain_suppress_exception_chaining(self):
314 with tt.AssertNotPrints("ZeroDivisionError"), \
318 with tt.AssertNotPrints("ZeroDivisionError"), \
315 tt.AssertPrints("ValueError", suppress=False):
319 tt.AssertPrints("ValueError", suppress=False):
316 ip.run_cell("%xmode Plain")
320 ip.run_cell("%xmode Plain")
317 ip.run_cell(self.SUPPRESS_CHAINING_CODE)
321 ip.run_cell(self.SUPPRESS_CHAINING_CODE)
318 ip.run_cell("%xmode Verbose")
322 ip.run_cell("%xmode Verbose")
319
323
320
324
321 class RecursionTest(unittest.TestCase):
325 class RecursionTest(unittest.TestCase):
322 DEFINITIONS = """
326 DEFINITIONS = """
323 def non_recurs():
327 def non_recurs():
324 1/0
328 1/0
325
329
326 def r1():
330 def r1():
327 r1()
331 r1()
328
332
329 def r3a():
333 def r3a():
330 r3b()
334 r3b()
331
335
332 def r3b():
336 def r3b():
333 r3c()
337 r3c()
334
338
335 def r3c():
339 def r3c():
336 r3a()
340 r3a()
337
341
338 def r3o1():
342 def r3o1():
339 r3a()
343 r3a()
340
344
341 def r3o2():
345 def r3o2():
342 r3o1()
346 r3o1()
343 """
347 """
344 def setUp(self):
348 def setUp(self):
345 ip.run_cell(self.DEFINITIONS)
349 ip.run_cell(self.DEFINITIONS)
346
350
347 def test_no_recursion(self):
351 def test_no_recursion(self):
348 with tt.AssertNotPrints("skipping similar frames"):
352 with tt.AssertNotPrints("skipping similar frames"):
349 ip.run_cell("non_recurs()")
353 ip.run_cell("non_recurs()")
350
354
351 @recursionlimit(200)
355 @recursionlimit(200)
352 def test_recursion_one_frame(self):
356 def test_recursion_one_frame(self):
353 with tt.AssertPrints(re.compile(
357 with tt.AssertPrints(re.compile(
354 r"\[\.\.\. skipping similar frames: r1 at line 5 \(\d{2,3} times\)\]")
358 r"\[\.\.\. skipping similar frames: r1 at line 5 \(\d{2,3} times\)\]")
355 ):
359 ):
356 ip.run_cell("r1()")
360 ip.run_cell("r1()")
357
361
358 @recursionlimit(200)
362 @recursionlimit(200)
359 def test_recursion_three_frames(self):
363 def test_recursion_three_frames(self):
360 with tt.AssertPrints("[... skipping similar frames: "), \
364 with tt.AssertPrints("[... skipping similar frames: "), \
361 tt.AssertPrints(re.compile(r"r3a at line 8 \(\d{2} times\)"), suppress=False), \
365 tt.AssertPrints(re.compile(r"r3a at line 8 \(\d{2} times\)"), suppress=False), \
362 tt.AssertPrints(re.compile(r"r3b at line 11 \(\d{2} times\)"), suppress=False), \
366 tt.AssertPrints(re.compile(r"r3b at line 11 \(\d{2} times\)"), suppress=False), \
363 tt.AssertPrints(re.compile(r"r3c at line 14 \(\d{2} times\)"), suppress=False):
367 tt.AssertPrints(re.compile(r"r3c at line 14 \(\d{2} times\)"), suppress=False):
364 ip.run_cell("r3o2()")
368 ip.run_cell("r3o2()")
365
369
366
370
367 #----------------------------------------------------------------------------
371 #----------------------------------------------------------------------------
368
372
369 # module testing (minimal)
373 # module testing (minimal)
370 def test_handlers():
374 def test_handlers():
371 def spam(c, d_e):
375 def spam(c, d_e):
372 (d, e) = d_e
376 (d, e) = d_e
373 x = c + d
377 x = c + d
374 y = c * d
378 y = c * d
375 foo(x, y)
379 foo(x, y)
376
380
377 def foo(a, b, bar=1):
381 def foo(a, b, bar=1):
378 eggs(a, b + bar)
382 eggs(a, b + bar)
379
383
380 def eggs(f, g, z=globals()):
384 def eggs(f, g, z=globals()):
381 h = f + g
385 h = f + g
382 i = f - g
386 i = f - g
383 return h / i
387 return h / i
384
388
385 buff = io.StringIO()
389 buff = io.StringIO()
386
390
387 buff.write('')
391 buff.write('')
388 buff.write('*** Before ***')
392 buff.write('*** Before ***')
389 try:
393 try:
390 buff.write(spam(1, (2, 3)))
394 buff.write(spam(1, (2, 3)))
391 except:
395 except:
392 traceback.print_exc(file=buff)
396 traceback.print_exc(file=buff)
393
397
394 handler = ColorTB(ostream=buff)
398 handler = ColorTB(ostream=buff)
395 buff.write('*** ColorTB ***')
399 buff.write('*** ColorTB ***')
396 try:
400 try:
397 buff.write(spam(1, (2, 3)))
401 buff.write(spam(1, (2, 3)))
398 except:
402 except:
399 handler(*sys.exc_info())
403 handler(*sys.exc_info())
400 buff.write('')
404 buff.write('')
401
405
402 handler = VerboseTB(ostream=buff)
406 handler = VerboseTB(ostream=buff)
403 buff.write('*** VerboseTB ***')
407 buff.write('*** VerboseTB ***')
404 try:
408 try:
405 buff.write(spam(1, (2, 3)))
409 buff.write(spam(1, (2, 3)))
406 except:
410 except:
407 handler(*sys.exc_info())
411 handler(*sys.exc_info())
408 buff.write('')
412 buff.write('')
General Comments 0
You need to be logged in to leave comments. Login now