##// END OF EJS Templates
Backport PR #12307: 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,466 +1,470 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 sys
6 import sys
7 import os.path
7 import os.path
8 from textwrap import dedent
8 from textwrap import dedent
9 import traceback
9 import traceback
10 import unittest
10 import unittest
11 from unittest import mock
11 from unittest import mock
12
12
13 import IPython.core.ultratb as ultratb
13 import IPython.core.ultratb as ultratb
14 from IPython.core.ultratb import ColorTB, VerboseTB, find_recursion
14 from IPython.core.ultratb import ColorTB, VerboseTB, find_recursion
15
15
16
16
17 from IPython.testing import tools as tt
17 from IPython.testing import tools as tt
18 from IPython.testing.decorators import onlyif_unicode_paths
18 from IPython.testing.decorators import onlyif_unicode_paths
19 from IPython.utils.syspathcontext import prepended_to_syspath
19 from IPython.utils.syspathcontext import prepended_to_syspath
20 from IPython.utils.tempdir import TemporaryDirectory
20 from IPython.utils.tempdir import TemporaryDirectory
21
21
22 file_1 = """1
22 file_1 = """1
23 2
23 2
24 3
24 3
25 def f():
25 def f():
26 1/0
26 1/0
27 """
27 """
28
28
29 file_2 = """def f():
29 file_2 = """def f():
30 1/0
30 1/0
31 """
31 """
32
32
33
33
34 def recursionlimit(frames):
34 def recursionlimit(frames):
35 """
35 """
36 decorator to set the recursion limit temporarily
36 decorator to set the recursion limit temporarily
37 """
37 """
38
38
39 def inner(test_function):
39 def inner(test_function):
40 def wrapper(*args, **kwargs):
40 def wrapper(*args, **kwargs):
41 _orig_rec_limit = ultratb._FRAME_RECURSION_LIMIT
41 _orig_rec_limit = ultratb._FRAME_RECURSION_LIMIT
42 ultratb._FRAME_RECURSION_LIMIT = 50
42 ultratb._FRAME_RECURSION_LIMIT = 50
43
43
44 rl = sys.getrecursionlimit()
44 rl = sys.getrecursionlimit()
45 sys.setrecursionlimit(frames)
45 sys.setrecursionlimit(frames)
46 try:
46 try:
47 return test_function(*args, **kwargs)
47 return test_function(*args, **kwargs)
48 finally:
48 finally:
49 sys.setrecursionlimit(rl)
49 sys.setrecursionlimit(rl)
50 ultratb._FRAME_RECURSION_LIMIT = _orig_rec_limit
50 ultratb._FRAME_RECURSION_LIMIT = _orig_rec_limit
51
51
52 return wrapper
52 return wrapper
53
53
54 return inner
54 return inner
55
55
56
56
57 class ChangedPyFileTest(unittest.TestCase):
57 class ChangedPyFileTest(unittest.TestCase):
58 def test_changing_py_file(self):
58 def test_changing_py_file(self):
59 """Traceback produced if the line where the error occurred is missing?
59 """Traceback produced if the line where the error occurred is missing?
60
60
61 https://github.com/ipython/ipython/issues/1456
61 https://github.com/ipython/ipython/issues/1456
62 """
62 """
63 with TemporaryDirectory() as td:
63 with TemporaryDirectory() as td:
64 fname = os.path.join(td, "foo.py")
64 fname = os.path.join(td, "foo.py")
65 with open(fname, "w") as f:
65 with open(fname, "w") as f:
66 f.write(file_1)
66 f.write(file_1)
67
67
68 with prepended_to_syspath(td):
68 with prepended_to_syspath(td):
69 ip.run_cell("import foo")
69 ip.run_cell("import foo")
70
70
71 with tt.AssertPrints("ZeroDivisionError"):
71 with tt.AssertPrints("ZeroDivisionError"):
72 ip.run_cell("foo.f()")
72 ip.run_cell("foo.f()")
73
73
74 # Make the file shorter, so the line of the error is missing.
74 # Make the file shorter, so the line of the error is missing.
75 with open(fname, "w") as f:
75 with open(fname, "w") as f:
76 f.write(file_2)
76 f.write(file_2)
77
77
78 # For some reason, this was failing on the *second* call after
78 # For some reason, this was failing on the *second* call after
79 # changing the file, so we call f() twice.
79 # changing the file, so we call f() twice.
80 with tt.AssertNotPrints("Internal Python error", channel='stderr'):
80 with tt.AssertNotPrints("Internal Python error", channel='stderr'):
81 with tt.AssertPrints("ZeroDivisionError"):
81 with tt.AssertPrints("ZeroDivisionError"):
82 ip.run_cell("foo.f()")
82 ip.run_cell("foo.f()")
83 with tt.AssertPrints("ZeroDivisionError"):
83 with tt.AssertPrints("ZeroDivisionError"):
84 ip.run_cell("foo.f()")
84 ip.run_cell("foo.f()")
85
85
86 iso_8859_5_file = u'''# coding: iso-8859-5
86 iso_8859_5_file = u'''# coding: iso-8859-5
87
87
88 def fail():
88 def fail():
89 """Π΄Π±Π˜Π–"""
89 """Π΄Π±Π˜Π–"""
90 1/0 # Π΄Π±Π˜Π–
90 1/0 # Π΄Π±Π˜Π–
91 '''
91 '''
92
92
93 class NonAsciiTest(unittest.TestCase):
93 class NonAsciiTest(unittest.TestCase):
94 @onlyif_unicode_paths
94 @onlyif_unicode_paths
95 def test_nonascii_path(self):
95 def test_nonascii_path(self):
96 # Non-ascii directory name as well.
96 # Non-ascii directory name as well.
97 with TemporaryDirectory(suffix=u'Γ©') as td:
97 with TemporaryDirectory(suffix=u'Γ©') as td:
98 fname = os.path.join(td, u"fooΓ©.py")
98 fname = os.path.join(td, u"fooΓ©.py")
99 with open(fname, "w") as f:
99 with open(fname, "w") as f:
100 f.write(file_1)
100 f.write(file_1)
101
101
102 with prepended_to_syspath(td):
102 with prepended_to_syspath(td):
103 ip.run_cell("import foo")
103 ip.run_cell("import foo")
104
104
105 with tt.AssertPrints("ZeroDivisionError"):
105 with tt.AssertPrints("ZeroDivisionError"):
106 ip.run_cell("foo.f()")
106 ip.run_cell("foo.f()")
107
107
108 def test_iso8859_5(self):
108 def test_iso8859_5(self):
109 with TemporaryDirectory() as td:
109 with TemporaryDirectory() as td:
110 fname = os.path.join(td, 'dfghjkl.py')
110 fname = os.path.join(td, 'dfghjkl.py')
111
111
112 with io.open(fname, 'w', encoding='iso-8859-5') as f:
112 with io.open(fname, 'w', encoding='iso-8859-5') as f:
113 f.write(iso_8859_5_file)
113 f.write(iso_8859_5_file)
114
114
115 with prepended_to_syspath(td):
115 with prepended_to_syspath(td):
116 ip.run_cell("from dfghjkl import fail")
116 ip.run_cell("from dfghjkl import fail")
117
117
118 with tt.AssertPrints("ZeroDivisionError"):
118 with tt.AssertPrints("ZeroDivisionError"):
119 with tt.AssertPrints(u'Π΄Π±Π˜Π–', suppress=False):
119 with tt.AssertPrints(u'Π΄Π±Π˜Π–', suppress=False):
120 ip.run_cell('fail()')
120 ip.run_cell('fail()')
121
121
122 def test_nonascii_msg(self):
122 def test_nonascii_msg(self):
123 cell = u"raise Exception('Γ©')"
123 cell = u"raise Exception('Γ©')"
124 expected = u"Exception('Γ©')"
124 expected = u"Exception('Γ©')"
125 ip.run_cell("%xmode plain")
125 ip.run_cell("%xmode plain")
126 with tt.AssertPrints(expected):
126 with tt.AssertPrints(expected):
127 ip.run_cell(cell)
127 ip.run_cell(cell)
128
128
129 ip.run_cell("%xmode verbose")
129 ip.run_cell("%xmode verbose")
130 with tt.AssertPrints(expected):
130 with tt.AssertPrints(expected):
131 ip.run_cell(cell)
131 ip.run_cell(cell)
132
132
133 ip.run_cell("%xmode context")
133 ip.run_cell("%xmode context")
134 with tt.AssertPrints(expected):
134 with tt.AssertPrints(expected):
135 ip.run_cell(cell)
135 ip.run_cell(cell)
136
136
137 ip.run_cell("%xmode minimal")
137 ip.run_cell("%xmode minimal")
138 with tt.AssertPrints(u"Exception: Γ©"):
138 with tt.AssertPrints(u"Exception: Γ©"):
139 ip.run_cell(cell)
139 ip.run_cell(cell)
140
140
141 # Put this back into Context mode for later tests.
141 # Put this back into Context mode for later tests.
142 ip.run_cell("%xmode context")
142 ip.run_cell("%xmode context")
143
143
144 class NestedGenExprTestCase(unittest.TestCase):
144 class NestedGenExprTestCase(unittest.TestCase):
145 """
145 """
146 Regression test for the following issues:
146 Regression test for the following issues:
147 https://github.com/ipython/ipython/issues/8293
147 https://github.com/ipython/ipython/issues/8293
148 https://github.com/ipython/ipython/issues/8205
148 https://github.com/ipython/ipython/issues/8205
149 """
149 """
150 def test_nested_genexpr(self):
150 def test_nested_genexpr(self):
151 code = dedent(
151 code = dedent(
152 """\
152 """\
153 class SpecificException(Exception):
153 class SpecificException(Exception):
154 pass
154 pass
155
155
156 def foo(x):
156 def foo(x):
157 raise SpecificException("Success!")
157 raise SpecificException("Success!")
158
158
159 sum(sum(foo(x) for _ in [0]) for x in [0])
159 sum(sum(foo(x) for _ in [0]) for x in [0])
160 """
160 """
161 )
161 )
162 with tt.AssertPrints('SpecificException: Success!', suppress=False):
162 with tt.AssertPrints('SpecificException: Success!', suppress=False):
163 ip.run_cell(code)
163 ip.run_cell(code)
164
164
165
165
166 indentationerror_file = """if True:
166 indentationerror_file = """if True:
167 zoon()
167 zoon()
168 """
168 """
169
169
170 class IndentationErrorTest(unittest.TestCase):
170 class IndentationErrorTest(unittest.TestCase):
171 def test_indentationerror_shows_line(self):
171 def test_indentationerror_shows_line(self):
172 # See issue gh-2398
172 # See issue gh-2398
173 with tt.AssertPrints("IndentationError"):
173 with tt.AssertPrints("IndentationError"):
174 with tt.AssertPrints("zoon()", suppress=False):
174 with tt.AssertPrints("zoon()", suppress=False):
175 ip.run_cell(indentationerror_file)
175 ip.run_cell(indentationerror_file)
176
176
177 with TemporaryDirectory() as td:
177 with TemporaryDirectory() as td:
178 fname = os.path.join(td, "foo.py")
178 fname = os.path.join(td, "foo.py")
179 with open(fname, "w") as f:
179 with open(fname, "w") as f:
180 f.write(indentationerror_file)
180 f.write(indentationerror_file)
181
181
182 with tt.AssertPrints("IndentationError"):
182 with tt.AssertPrints("IndentationError"):
183 with tt.AssertPrints("zoon()", suppress=False):
183 with tt.AssertPrints("zoon()", suppress=False):
184 ip.magic('run %s' % fname)
184 ip.magic('run %s' % fname)
185
185
186 se_file_1 = """1
186 se_file_1 = """1
187 2
187 2
188 7/
188 7/
189 """
189 """
190
190
191 se_file_2 = """7/
191 se_file_2 = """7/
192 """
192 """
193
193
194 class SyntaxErrorTest(unittest.TestCase):
194 class SyntaxErrorTest(unittest.TestCase):
195 def test_syntaxerror_without_lineno(self):
195 def test_syntaxerror_without_lineno(self):
196 with tt.AssertNotPrints("TypeError"):
196 with tt.AssertNotPrints("TypeError"):
197 with tt.AssertPrints("line unknown"):
197 with tt.AssertPrints("line unknown"):
198 ip.run_cell("raise SyntaxError()")
198 ip.run_cell("raise SyntaxError()")
199
199
200 def test_syntaxerror_no_stacktrace_at_compile_time(self):
200 def test_syntaxerror_no_stacktrace_at_compile_time(self):
201 syntax_error_at_compile_time = """
201 syntax_error_at_compile_time = """
202 def foo():
202 def foo():
203 ..
203 ..
204 """
204 """
205 with tt.AssertPrints("SyntaxError"):
205 with tt.AssertPrints("SyntaxError"):
206 ip.run_cell(syntax_error_at_compile_time)
206 ip.run_cell(syntax_error_at_compile_time)
207
207
208 with tt.AssertNotPrints("foo()"):
208 with tt.AssertNotPrints("foo()"):
209 ip.run_cell(syntax_error_at_compile_time)
209 ip.run_cell(syntax_error_at_compile_time)
210
210
211 def test_syntaxerror_stacktrace_when_running_compiled_code(self):
211 def test_syntaxerror_stacktrace_when_running_compiled_code(self):
212 syntax_error_at_runtime = """
212 syntax_error_at_runtime = """
213 def foo():
213 def foo():
214 eval("..")
214 eval("..")
215
215
216 def bar():
216 def bar():
217 foo()
217 foo()
218
218
219 bar()
219 bar()
220 """
220 """
221 with tt.AssertPrints("SyntaxError"):
221 with tt.AssertPrints("SyntaxError"):
222 ip.run_cell(syntax_error_at_runtime)
222 ip.run_cell(syntax_error_at_runtime)
223 # Assert syntax error during runtime generate stacktrace
223 # Assert syntax error during runtime generate stacktrace
224 with tt.AssertPrints(["foo()", "bar()"]):
224 with tt.AssertPrints(["foo()", "bar()"]):
225 ip.run_cell(syntax_error_at_runtime)
225 ip.run_cell(syntax_error_at_runtime)
226 del ip.user_ns['bar']
226 del ip.user_ns['bar']
227 del ip.user_ns['foo']
227 del ip.user_ns['foo']
228
228
229 def test_changing_py_file(self):
229 def test_changing_py_file(self):
230 with TemporaryDirectory() as td:
230 with TemporaryDirectory() as td:
231 fname = os.path.join(td, "foo.py")
231 fname = os.path.join(td, "foo.py")
232 with open(fname, 'w') as f:
232 with open(fname, 'w') as f:
233 f.write(se_file_1)
233 f.write(se_file_1)
234
234
235 with tt.AssertPrints(["7/", "SyntaxError"]):
235 with tt.AssertPrints(["7/", "SyntaxError"]):
236 ip.magic("run " + fname)
236 ip.magic("run " + fname)
237
237
238 # Modify the file
238 # Modify the file
239 with open(fname, 'w') as f:
239 with open(fname, 'w') as f:
240 f.write(se_file_2)
240 f.write(se_file_2)
241
241
242 # The SyntaxError should point to the correct line
242 # The SyntaxError should point to the correct line
243 with tt.AssertPrints(["7/", "SyntaxError"]):
243 with tt.AssertPrints(["7/", "SyntaxError"]):
244 ip.magic("run " + fname)
244 ip.magic("run " + fname)
245
245
246 def test_non_syntaxerror(self):
246 def test_non_syntaxerror(self):
247 # SyntaxTB may be called with an error other than a SyntaxError
247 # SyntaxTB may be called with an error other than a SyntaxError
248 # See e.g. gh-4361
248 # See e.g. gh-4361
249 try:
249 try:
250 raise ValueError('QWERTY')
250 raise ValueError('QWERTY')
251 except ValueError:
251 except ValueError:
252 with tt.AssertPrints('QWERTY'):
252 with tt.AssertPrints('QWERTY'):
253 ip.showsyntaxerror()
253 ip.showsyntaxerror()
254
254
255
255 import sys
256 class MemoryErrorTest(unittest.TestCase):
256 if sys.version_info < (3,9):
257 def test_memoryerror(self):
257 """
258 memoryerror_code = "(" * 200 + ")" * 200
258 New 3.9 Pgen Parser does not raise Memory error, except on failed malloc.
259 with tt.AssertPrints("MemoryError"):
259 """
260 ip.run_cell(memoryerror_code)
260 class MemoryErrorTest(unittest.TestCase):
261 def test_memoryerror(self):
262 memoryerror_code = "(" * 200 + ")" * 200
263 with tt.AssertPrints("MemoryError"):
264 ip.run_cell(memoryerror_code)
261
265
262
266
263 class Python3ChainedExceptionsTest(unittest.TestCase):
267 class Python3ChainedExceptionsTest(unittest.TestCase):
264 DIRECT_CAUSE_ERROR_CODE = """
268 DIRECT_CAUSE_ERROR_CODE = """
265 try:
269 try:
266 x = 1 + 2
270 x = 1 + 2
267 print(not_defined_here)
271 print(not_defined_here)
268 except Exception as e:
272 except Exception as e:
269 x += 55
273 x += 55
270 x - 1
274 x - 1
271 y = {}
275 y = {}
272 raise KeyError('uh') from e
276 raise KeyError('uh') from e
273 """
277 """
274
278
275 EXCEPTION_DURING_HANDLING_CODE = """
279 EXCEPTION_DURING_HANDLING_CODE = """
276 try:
280 try:
277 x = 1 + 2
281 x = 1 + 2
278 print(not_defined_here)
282 print(not_defined_here)
279 except Exception as e:
283 except Exception as e:
280 x += 55
284 x += 55
281 x - 1
285 x - 1
282 y = {}
286 y = {}
283 raise KeyError('uh')
287 raise KeyError('uh')
284 """
288 """
285
289
286 SUPPRESS_CHAINING_CODE = """
290 SUPPRESS_CHAINING_CODE = """
287 try:
291 try:
288 1/0
292 1/0
289 except Exception:
293 except Exception:
290 raise ValueError("Yikes") from None
294 raise ValueError("Yikes") from None
291 """
295 """
292
296
293 def test_direct_cause_error(self):
297 def test_direct_cause_error(self):
294 with tt.AssertPrints(["KeyError", "NameError", "direct cause"]):
298 with tt.AssertPrints(["KeyError", "NameError", "direct cause"]):
295 ip.run_cell(self.DIRECT_CAUSE_ERROR_CODE)
299 ip.run_cell(self.DIRECT_CAUSE_ERROR_CODE)
296
300
297 def test_exception_during_handling_error(self):
301 def test_exception_during_handling_error(self):
298 with tt.AssertPrints(["KeyError", "NameError", "During handling"]):
302 with tt.AssertPrints(["KeyError", "NameError", "During handling"]):
299 ip.run_cell(self.EXCEPTION_DURING_HANDLING_CODE)
303 ip.run_cell(self.EXCEPTION_DURING_HANDLING_CODE)
300
304
301 def test_suppress_exception_chaining(self):
305 def test_suppress_exception_chaining(self):
302 with tt.AssertNotPrints("ZeroDivisionError"), \
306 with tt.AssertNotPrints("ZeroDivisionError"), \
303 tt.AssertPrints("ValueError", suppress=False):
307 tt.AssertPrints("ValueError", suppress=False):
304 ip.run_cell(self.SUPPRESS_CHAINING_CODE)
308 ip.run_cell(self.SUPPRESS_CHAINING_CODE)
305
309
306 def test_plain_direct_cause_error(self):
310 def test_plain_direct_cause_error(self):
307 with tt.AssertPrints(["KeyError", "NameError", "direct cause"]):
311 with tt.AssertPrints(["KeyError", "NameError", "direct cause"]):
308 ip.run_cell("%xmode Plain")
312 ip.run_cell("%xmode Plain")
309 ip.run_cell(self.DIRECT_CAUSE_ERROR_CODE)
313 ip.run_cell(self.DIRECT_CAUSE_ERROR_CODE)
310 ip.run_cell("%xmode Verbose")
314 ip.run_cell("%xmode Verbose")
311
315
312 def test_plain_exception_during_handling_error(self):
316 def test_plain_exception_during_handling_error(self):
313 with tt.AssertPrints(["KeyError", "NameError", "During handling"]):
317 with tt.AssertPrints(["KeyError", "NameError", "During handling"]):
314 ip.run_cell("%xmode Plain")
318 ip.run_cell("%xmode Plain")
315 ip.run_cell(self.EXCEPTION_DURING_HANDLING_CODE)
319 ip.run_cell(self.EXCEPTION_DURING_HANDLING_CODE)
316 ip.run_cell("%xmode Verbose")
320 ip.run_cell("%xmode Verbose")
317
321
318 def test_plain_suppress_exception_chaining(self):
322 def test_plain_suppress_exception_chaining(self):
319 with tt.AssertNotPrints("ZeroDivisionError"), \
323 with tt.AssertNotPrints("ZeroDivisionError"), \
320 tt.AssertPrints("ValueError", suppress=False):
324 tt.AssertPrints("ValueError", suppress=False):
321 ip.run_cell("%xmode Plain")
325 ip.run_cell("%xmode Plain")
322 ip.run_cell(self.SUPPRESS_CHAINING_CODE)
326 ip.run_cell(self.SUPPRESS_CHAINING_CODE)
323 ip.run_cell("%xmode Verbose")
327 ip.run_cell("%xmode Verbose")
324
328
325
329
326 class RecursionTest(unittest.TestCase):
330 class RecursionTest(unittest.TestCase):
327 DEFINITIONS = """
331 DEFINITIONS = """
328 def non_recurs():
332 def non_recurs():
329 1/0
333 1/0
330
334
331 def r1():
335 def r1():
332 r1()
336 r1()
333
337
334 def r3a():
338 def r3a():
335 r3b()
339 r3b()
336
340
337 def r3b():
341 def r3b():
338 r3c()
342 r3c()
339
343
340 def r3c():
344 def r3c():
341 r3a()
345 r3a()
342
346
343 def r3o1():
347 def r3o1():
344 r3a()
348 r3a()
345
349
346 def r3o2():
350 def r3o2():
347 r3o1()
351 r3o1()
348 """
352 """
349 def setUp(self):
353 def setUp(self):
350 ip.run_cell(self.DEFINITIONS)
354 ip.run_cell(self.DEFINITIONS)
351
355
352 def test_no_recursion(self):
356 def test_no_recursion(self):
353 with tt.AssertNotPrints("frames repeated"):
357 with tt.AssertNotPrints("frames repeated"):
354 ip.run_cell("non_recurs()")
358 ip.run_cell("non_recurs()")
355
359
356 @recursionlimit(150)
360 @recursionlimit(150)
357 def test_recursion_one_frame(self):
361 def test_recursion_one_frame(self):
358 with tt.AssertPrints("1 frames repeated"):
362 with tt.AssertPrints("1 frames repeated"):
359 ip.run_cell("r1()")
363 ip.run_cell("r1()")
360
364
361 @recursionlimit(150)
365 @recursionlimit(150)
362 def test_recursion_three_frames(self):
366 def test_recursion_three_frames(self):
363 with tt.AssertPrints("3 frames repeated"):
367 with tt.AssertPrints("3 frames repeated"):
364 ip.run_cell("r3o2()")
368 ip.run_cell("r3o2()")
365
369
366 @recursionlimit(150)
370 @recursionlimit(150)
367 def test_find_recursion(self):
371 def test_find_recursion(self):
368 captured = []
372 captured = []
369 def capture_exc(*args, **kwargs):
373 def capture_exc(*args, **kwargs):
370 captured.append(sys.exc_info())
374 captured.append(sys.exc_info())
371 with mock.patch.object(ip, 'showtraceback', capture_exc):
375 with mock.patch.object(ip, 'showtraceback', capture_exc):
372 ip.run_cell("r3o2()")
376 ip.run_cell("r3o2()")
373
377
374 self.assertEqual(len(captured), 1)
378 self.assertEqual(len(captured), 1)
375 etype, evalue, tb = captured[0]
379 etype, evalue, tb = captured[0]
376 self.assertIn("recursion", str(evalue))
380 self.assertIn("recursion", str(evalue))
377
381
378 records = ip.InteractiveTB.get_records(tb, 3, ip.InteractiveTB.tb_offset)
382 records = ip.InteractiveTB.get_records(tb, 3, ip.InteractiveTB.tb_offset)
379 for r in records[:10]:
383 for r in records[:10]:
380 print(r[1:4])
384 print(r[1:4])
381
385
382 # The outermost frames should be:
386 # The outermost frames should be:
383 # 0: the 'cell' that was running when the exception came up
387 # 0: the 'cell' that was running when the exception came up
384 # 1: r3o2()
388 # 1: r3o2()
385 # 2: r3o1()
389 # 2: r3o1()
386 # 3: r3a()
390 # 3: r3a()
387 # Then repeating r3b, r3c, r3a
391 # Then repeating r3b, r3c, r3a
388 last_unique, repeat_length = find_recursion(etype, evalue, records)
392 last_unique, repeat_length = find_recursion(etype, evalue, records)
389 self.assertEqual(last_unique, 2)
393 self.assertEqual(last_unique, 2)
390 self.assertEqual(repeat_length, 3)
394 self.assertEqual(repeat_length, 3)
391
395
392
396
393 #----------------------------------------------------------------------------
397 #----------------------------------------------------------------------------
394
398
395 # module testing (minimal)
399 # module testing (minimal)
396 def test_handlers():
400 def test_handlers():
397 def spam(c, d_e):
401 def spam(c, d_e):
398 (d, e) = d_e
402 (d, e) = d_e
399 x = c + d
403 x = c + d
400 y = c * d
404 y = c * d
401 foo(x, y)
405 foo(x, y)
402
406
403 def foo(a, b, bar=1):
407 def foo(a, b, bar=1):
404 eggs(a, b + bar)
408 eggs(a, b + bar)
405
409
406 def eggs(f, g, z=globals()):
410 def eggs(f, g, z=globals()):
407 h = f + g
411 h = f + g
408 i = f - g
412 i = f - g
409 return h / i
413 return h / i
410
414
411 buff = io.StringIO()
415 buff = io.StringIO()
412
416
413 buff.write('')
417 buff.write('')
414 buff.write('*** Before ***')
418 buff.write('*** Before ***')
415 try:
419 try:
416 buff.write(spam(1, (2, 3)))
420 buff.write(spam(1, (2, 3)))
417 except:
421 except:
418 traceback.print_exc(file=buff)
422 traceback.print_exc(file=buff)
419
423
420 handler = ColorTB(ostream=buff)
424 handler = ColorTB(ostream=buff)
421 buff.write('*** ColorTB ***')
425 buff.write('*** ColorTB ***')
422 try:
426 try:
423 buff.write(spam(1, (2, 3)))
427 buff.write(spam(1, (2, 3)))
424 except:
428 except:
425 handler(*sys.exc_info())
429 handler(*sys.exc_info())
426 buff.write('')
430 buff.write('')
427
431
428 handler = VerboseTB(ostream=buff)
432 handler = VerboseTB(ostream=buff)
429 buff.write('*** VerboseTB ***')
433 buff.write('*** VerboseTB ***')
430 try:
434 try:
431 buff.write(spam(1, (2, 3)))
435 buff.write(spam(1, (2, 3)))
432 except:
436 except:
433 handler(*sys.exc_info())
437 handler(*sys.exc_info())
434 buff.write('')
438 buff.write('')
435
439
436 from IPython.testing.decorators import skipif
440 from IPython.testing.decorators import skipif
437
441
438 class TokenizeFailureTest(unittest.TestCase):
442 class TokenizeFailureTest(unittest.TestCase):
439 """Tests related to https://github.com/ipython/ipython/issues/6864."""
443 """Tests related to https://github.com/ipython/ipython/issues/6864."""
440
444
441 # that appear to test that we are handling an exception that can be thrown
445 # that appear to test that we are handling an exception that can be thrown
442 # by the tokenizer due to a bug that seem to have been fixed in 3.8, though
446 # by the tokenizer due to a bug that seem to have been fixed in 3.8, though
443 # I'm unsure if other sequences can make it raise this error. Let's just
447 # I'm unsure if other sequences can make it raise this error. Let's just
444 # skip in 3.8 for now
448 # skip in 3.8 for now
445 @skipif(sys.version_info > (3,8))
449 @skipif(sys.version_info > (3,8))
446 def testLogging(self):
450 def testLogging(self):
447 message = "An unexpected error occurred while tokenizing input"
451 message = "An unexpected error occurred while tokenizing input"
448 cell = 'raise ValueError("""a\nb""")'
452 cell = 'raise ValueError("""a\nb""")'
449
453
450 stream = io.StringIO()
454 stream = io.StringIO()
451 handler = logging.StreamHandler(stream)
455 handler = logging.StreamHandler(stream)
452 logger = logging.getLogger()
456 logger = logging.getLogger()
453 loglevel = logger.level
457 loglevel = logger.level
454 logger.addHandler(handler)
458 logger.addHandler(handler)
455 self.addCleanup(lambda: logger.removeHandler(handler))
459 self.addCleanup(lambda: logger.removeHandler(handler))
456 self.addCleanup(lambda: logger.setLevel(loglevel))
460 self.addCleanup(lambda: logger.setLevel(loglevel))
457
461
458 logger.setLevel(logging.INFO)
462 logger.setLevel(logging.INFO)
459 with tt.AssertNotPrints(message):
463 with tt.AssertNotPrints(message):
460 ip.run_cell(cell)
464 ip.run_cell(cell)
461 self.assertNotIn(message, stream.getvalue())
465 self.assertNotIn(message, stream.getvalue())
462
466
463 logger.setLevel(logging.DEBUG)
467 logger.setLevel(logging.DEBUG)
464 with tt.AssertNotPrints(message):
468 with tt.AssertNotPrints(message):
465 ip.run_cell(cell)
469 ip.run_cell(cell)
466 self.assertIn(message, stream.getvalue())
470 self.assertIn(message, stream.getvalue())
General Comments 0
You need to be logged in to leave comments. Login now