##// END OF EJS Templates
Fix typos
Andrew Kreimer -
Show More
@@ -1,1769 +1,1769
1 # encoding: utf-8
1 # encoding: utf-8
2 """Tests for the IPython tab-completion machinery."""
2 """Tests for the IPython tab-completion machinery."""
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 import os
7 import os
8 import pytest
8 import pytest
9 import sys
9 import sys
10 import textwrap
10 import textwrap
11 import unittest
11 import unittest
12
12
13 from importlib.metadata import version
13 from importlib.metadata import version
14
14
15
15
16 from contextlib import contextmanager
16 from contextlib import contextmanager
17
17
18 from traitlets.config.loader import Config
18 from traitlets.config.loader import Config
19 from IPython import get_ipython
19 from IPython import get_ipython
20 from IPython.core import completer
20 from IPython.core import completer
21 from IPython.utils.tempdir import TemporaryDirectory, TemporaryWorkingDirectory
21 from IPython.utils.tempdir import TemporaryDirectory, TemporaryWorkingDirectory
22 from IPython.utils.generics import complete_object
22 from IPython.utils.generics import complete_object
23 from IPython.testing import decorators as dec
23 from IPython.testing import decorators as dec
24
24
25 from IPython.core.completer import (
25 from IPython.core.completer import (
26 Completion,
26 Completion,
27 provisionalcompleter,
27 provisionalcompleter,
28 match_dict_keys,
28 match_dict_keys,
29 _deduplicate_completions,
29 _deduplicate_completions,
30 _match_number_in_dict_key_prefix,
30 _match_number_in_dict_key_prefix,
31 completion_matcher,
31 completion_matcher,
32 SimpleCompletion,
32 SimpleCompletion,
33 CompletionContext,
33 CompletionContext,
34 )
34 )
35
35
36 from packaging.version import parse
36 from packaging.version import parse
37
37
38
38
39 # -----------------------------------------------------------------------------
39 # -----------------------------------------------------------------------------
40 # Test functions
40 # Test functions
41 # -----------------------------------------------------------------------------
41 # -----------------------------------------------------------------------------
42
42
43
43
44 def recompute_unicode_ranges():
44 def recompute_unicode_ranges():
45 """
45 """
46 utility to recompute the largest unicode range without any characters
46 utility to recompute the largest unicode range without any characters
47
47
48 use to recompute the gap in the global _UNICODE_RANGES of completer.py
48 use to recompute the gap in the global _UNICODE_RANGES of completer.py
49 """
49 """
50 import itertools
50 import itertools
51 import unicodedata
51 import unicodedata
52
52
53 valid = []
53 valid = []
54 for c in range(0, 0x10FFFF + 1):
54 for c in range(0, 0x10FFFF + 1):
55 try:
55 try:
56 unicodedata.name(chr(c))
56 unicodedata.name(chr(c))
57 except ValueError:
57 except ValueError:
58 continue
58 continue
59 valid.append(c)
59 valid.append(c)
60
60
61 def ranges(i):
61 def ranges(i):
62 for a, b in itertools.groupby(enumerate(i), lambda pair: pair[1] - pair[0]):
62 for a, b in itertools.groupby(enumerate(i), lambda pair: pair[1] - pair[0]):
63 b = list(b)
63 b = list(b)
64 yield b[0][1], b[-1][1]
64 yield b[0][1], b[-1][1]
65
65
66 rg = list(ranges(valid))
66 rg = list(ranges(valid))
67 lens = []
67 lens = []
68 gap_lens = []
68 gap_lens = []
69 pstart, pstop = 0, 0
69 pstart, pstop = 0, 0
70 for start, stop in rg:
70 for start, stop in rg:
71 lens.append(stop - start)
71 lens.append(stop - start)
72 gap_lens.append(
72 gap_lens.append(
73 (
73 (
74 start - pstop,
74 start - pstop,
75 hex(pstop + 1),
75 hex(pstop + 1),
76 hex(start),
76 hex(start),
77 f"{round((start - pstop)/0xe01f0*100)}%",
77 f"{round((start - pstop)/0xe01f0*100)}%",
78 )
78 )
79 )
79 )
80 pstart, pstop = start, stop
80 pstart, pstop = start, stop
81
81
82 return sorted(gap_lens)[-1]
82 return sorted(gap_lens)[-1]
83
83
84
84
85 def test_unicode_range():
85 def test_unicode_range():
86 """
86 """
87 Test that the ranges we test for unicode names give the same number of
87 Test that the ranges we test for unicode names give the same number of
88 results than testing the full length.
88 results than testing the full length.
89 """
89 """
90 from IPython.core.completer import _unicode_name_compute, _UNICODE_RANGES
90 from IPython.core.completer import _unicode_name_compute, _UNICODE_RANGES
91
91
92 expected_list = _unicode_name_compute([(0, 0x110000)])
92 expected_list = _unicode_name_compute([(0, 0x110000)])
93 test = _unicode_name_compute(_UNICODE_RANGES)
93 test = _unicode_name_compute(_UNICODE_RANGES)
94 len_exp = len(expected_list)
94 len_exp = len(expected_list)
95 len_test = len(test)
95 len_test = len(test)
96
96
97 # do not inline the len() or on error pytest will try to print the 130 000 +
97 # do not inline the len() or on error pytest will try to print the 130 000 +
98 # elements.
98 # elements.
99 message = None
99 message = None
100 if len_exp != len_test or len_exp > 131808:
100 if len_exp != len_test or len_exp > 131808:
101 size, start, stop, prct = recompute_unicode_ranges()
101 size, start, stop, prct = recompute_unicode_ranges()
102 message = f"""_UNICODE_RANGES likely wrong and need updating. This is
102 message = f"""_UNICODE_RANGES likely wrong and need updating. This is
103 likely due to a new release of Python. We've find that the biggest gap
103 likely due to a new release of Python. We've find that the biggest gap
104 in unicode characters has reduces in size to be {size} characters
104 in unicode characters has reduces in size to be {size} characters
105 ({prct}), from {start}, to {stop}. In completer.py likely update to
105 ({prct}), from {start}, to {stop}. In completer.py likely update to
106
106
107 _UNICODE_RANGES = [(32, {start}), ({stop}, 0xe01f0)]
107 _UNICODE_RANGES = [(32, {start}), ({stop}, 0xe01f0)]
108
108
109 And update the assertion below to use
109 And update the assertion below to use
110
110
111 len_exp <= {len_exp}
111 len_exp <= {len_exp}
112 """
112 """
113 assert len_exp == len_test, message
113 assert len_exp == len_test, message
114
114
115 # fail if new unicode symbols have been added.
115 # fail if new unicode symbols have been added.
116 assert len_exp <= 143668, message
116 assert len_exp <= 143668, message
117
117
118
118
119 @contextmanager
119 @contextmanager
120 def greedy_completion():
120 def greedy_completion():
121 ip = get_ipython()
121 ip = get_ipython()
122 greedy_original = ip.Completer.greedy
122 greedy_original = ip.Completer.greedy
123 try:
123 try:
124 ip.Completer.greedy = True
124 ip.Completer.greedy = True
125 yield
125 yield
126 finally:
126 finally:
127 ip.Completer.greedy = greedy_original
127 ip.Completer.greedy = greedy_original
128
128
129
129
130 @contextmanager
130 @contextmanager
131 def evaluation_policy(evaluation: str):
131 def evaluation_policy(evaluation: str):
132 ip = get_ipython()
132 ip = get_ipython()
133 evaluation_original = ip.Completer.evaluation
133 evaluation_original = ip.Completer.evaluation
134 try:
134 try:
135 ip.Completer.evaluation = evaluation
135 ip.Completer.evaluation = evaluation
136 yield
136 yield
137 finally:
137 finally:
138 ip.Completer.evaluation = evaluation_original
138 ip.Completer.evaluation = evaluation_original
139
139
140
140
141 @contextmanager
141 @contextmanager
142 def custom_matchers(matchers):
142 def custom_matchers(matchers):
143 ip = get_ipython()
143 ip = get_ipython()
144 try:
144 try:
145 ip.Completer.custom_matchers.extend(matchers)
145 ip.Completer.custom_matchers.extend(matchers)
146 yield
146 yield
147 finally:
147 finally:
148 ip.Completer.custom_matchers.clear()
148 ip.Completer.custom_matchers.clear()
149
149
150
150
151 def test_protect_filename():
151 def test_protect_filename():
152 if sys.platform == "win32":
152 if sys.platform == "win32":
153 pairs = [
153 pairs = [
154 ("abc", "abc"),
154 ("abc", "abc"),
155 (" abc", '" abc"'),
155 (" abc", '" abc"'),
156 ("a bc", '"a bc"'),
156 ("a bc", '"a bc"'),
157 ("a bc", '"a bc"'),
157 ("a bc", '"a bc"'),
158 (" bc", '" bc"'),
158 (" bc", '" bc"'),
159 ]
159 ]
160 else:
160 else:
161 pairs = [
161 pairs = [
162 ("abc", "abc"),
162 ("abc", "abc"),
163 (" abc", r"\ abc"),
163 (" abc", r"\ abc"),
164 ("a bc", r"a\ bc"),
164 ("a bc", r"a\ bc"),
165 ("a bc", r"a\ \ bc"),
165 ("a bc", r"a\ \ bc"),
166 (" bc", r"\ \ bc"),
166 (" bc", r"\ \ bc"),
167 # On posix, we also protect parens and other special characters.
167 # On posix, we also protect parens and other special characters.
168 ("a(bc", r"a\(bc"),
168 ("a(bc", r"a\(bc"),
169 ("a)bc", r"a\)bc"),
169 ("a)bc", r"a\)bc"),
170 ("a( )bc", r"a\(\ \)bc"),
170 ("a( )bc", r"a\(\ \)bc"),
171 ("a[1]bc", r"a\[1\]bc"),
171 ("a[1]bc", r"a\[1\]bc"),
172 ("a{1}bc", r"a\{1\}bc"),
172 ("a{1}bc", r"a\{1\}bc"),
173 ("a#bc", r"a\#bc"),
173 ("a#bc", r"a\#bc"),
174 ("a?bc", r"a\?bc"),
174 ("a?bc", r"a\?bc"),
175 ("a=bc", r"a\=bc"),
175 ("a=bc", r"a\=bc"),
176 ("a\\bc", r"a\\bc"),
176 ("a\\bc", r"a\\bc"),
177 ("a|bc", r"a\|bc"),
177 ("a|bc", r"a\|bc"),
178 ("a;bc", r"a\;bc"),
178 ("a;bc", r"a\;bc"),
179 ("a:bc", r"a\:bc"),
179 ("a:bc", r"a\:bc"),
180 ("a'bc", r"a\'bc"),
180 ("a'bc", r"a\'bc"),
181 ("a*bc", r"a\*bc"),
181 ("a*bc", r"a\*bc"),
182 ('a"bc', r"a\"bc"),
182 ('a"bc', r"a\"bc"),
183 ("a^bc", r"a\^bc"),
183 ("a^bc", r"a\^bc"),
184 ("a&bc", r"a\&bc"),
184 ("a&bc", r"a\&bc"),
185 ]
185 ]
186 # run the actual tests
186 # run the actual tests
187 for s1, s2 in pairs:
187 for s1, s2 in pairs:
188 s1p = completer.protect_filename(s1)
188 s1p = completer.protect_filename(s1)
189 assert s1p == s2
189 assert s1p == s2
190
190
191
191
192 def check_line_split(splitter, test_specs):
192 def check_line_split(splitter, test_specs):
193 for part1, part2, split in test_specs:
193 for part1, part2, split in test_specs:
194 cursor_pos = len(part1)
194 cursor_pos = len(part1)
195 line = part1 + part2
195 line = part1 + part2
196 out = splitter.split_line(line, cursor_pos)
196 out = splitter.split_line(line, cursor_pos)
197 assert out == split
197 assert out == split
198
198
199 def test_line_split():
199 def test_line_split():
200 """Basic line splitter test with default specs."""
200 """Basic line splitter test with default specs."""
201 sp = completer.CompletionSplitter()
201 sp = completer.CompletionSplitter()
202 # The format of the test specs is: part1, part2, expected answer. Parts 1
202 # The format of the test specs is: part1, part2, expected answer. Parts 1
203 # and 2 are joined into the 'line' sent to the splitter, as if the cursor
203 # and 2 are joined into the 'line' sent to the splitter, as if the cursor
204 # was at the end of part1. So an empty part2 represents someone hitting
204 # was at the end of part1. So an empty part2 represents someone hitting
205 # tab at the end of the line, the most common case.
205 # tab at the end of the line, the most common case.
206 t = [
206 t = [
207 ("run some/scrip", "", "some/scrip"),
207 ("run some/script", "", "some/script"),
208 ("run scripts/er", "ror.py foo", "scripts/er"),
208 ("run scripts/er", "ror.py foo", "scripts/er"),
209 ("echo $HOM", "", "HOM"),
209 ("echo $HOM", "", "HOM"),
210 ("print sys.pa", "", "sys.pa"),
210 ("print sys.pa", "", "sys.pa"),
211 ("print(sys.pa", "", "sys.pa"),
211 ("print(sys.pa", "", "sys.pa"),
212 ("execfile('scripts/er", "", "scripts/er"),
212 ("execfile('scripts/er", "", "scripts/er"),
213 ("a[x.", "", "x."),
213 ("a[x.", "", "x."),
214 ("a[x.", "y", "x."),
214 ("a[x.", "y", "x."),
215 ('cd "some_file/', "", "some_file/"),
215 ('cd "some_file/', "", "some_file/"),
216 ]
216 ]
217 check_line_split(sp, t)
217 check_line_split(sp, t)
218 # Ensure splitting works OK with unicode by re-running the tests with
218 # Ensure splitting works OK with unicode by re-running the tests with
219 # all inputs turned into unicode
219 # all inputs turned into unicode
220 check_line_split(sp, [map(str, p) for p in t])
220 check_line_split(sp, [map(str, p) for p in t])
221
221
222
222
223 class NamedInstanceClass:
223 class NamedInstanceClass:
224 instances = {}
224 instances = {}
225
225
226 def __init__(self, name):
226 def __init__(self, name):
227 self.instances[name] = self
227 self.instances[name] = self
228
228
229 @classmethod
229 @classmethod
230 def _ipython_key_completions_(cls):
230 def _ipython_key_completions_(cls):
231 return cls.instances.keys()
231 return cls.instances.keys()
232
232
233
233
234 class KeyCompletable:
234 class KeyCompletable:
235 def __init__(self, things=()):
235 def __init__(self, things=()):
236 self.things = things
236 self.things = things
237
237
238 def _ipython_key_completions_(self):
238 def _ipython_key_completions_(self):
239 return list(self.things)
239 return list(self.things)
240
240
241
241
242 class TestCompleter(unittest.TestCase):
242 class TestCompleter(unittest.TestCase):
243 def setUp(self):
243 def setUp(self):
244 """
244 """
245 We want to silence all PendingDeprecationWarning when testing the completer
245 We want to silence all PendingDeprecationWarning when testing the completer
246 """
246 """
247 self._assertwarns = self.assertWarns(PendingDeprecationWarning)
247 self._assertwarns = self.assertWarns(PendingDeprecationWarning)
248 self._assertwarns.__enter__()
248 self._assertwarns.__enter__()
249
249
250 def tearDown(self):
250 def tearDown(self):
251 try:
251 try:
252 self._assertwarns.__exit__(None, None, None)
252 self._assertwarns.__exit__(None, None, None)
253 except AssertionError:
253 except AssertionError:
254 pass
254 pass
255
255
256 def test_custom_completion_error(self):
256 def test_custom_completion_error(self):
257 """Test that errors from custom attribute completers are silenced."""
257 """Test that errors from custom attribute completers are silenced."""
258 ip = get_ipython()
258 ip = get_ipython()
259
259
260 class A:
260 class A:
261 pass
261 pass
262
262
263 ip.user_ns["x"] = A()
263 ip.user_ns["x"] = A()
264
264
265 @complete_object.register(A)
265 @complete_object.register(A)
266 def complete_A(a, existing_completions):
266 def complete_A(a, existing_completions):
267 raise TypeError("this should be silenced")
267 raise TypeError("this should be silenced")
268
268
269 ip.complete("x.")
269 ip.complete("x.")
270
270
271 def test_custom_completion_ordering(self):
271 def test_custom_completion_ordering(self):
272 """Test that errors from custom attribute completers are silenced."""
272 """Test that errors from custom attribute completers are silenced."""
273 ip = get_ipython()
273 ip = get_ipython()
274
274
275 _, matches = ip.complete('in')
275 _, matches = ip.complete('in')
276 assert matches.index('input') < matches.index('int')
276 assert matches.index('input') < matches.index('int')
277
277
278 def complete_example(a):
278 def complete_example(a):
279 return ['example2', 'example1']
279 return ['example2', 'example1']
280
280
281 ip.Completer.custom_completers.add_re('ex*', complete_example)
281 ip.Completer.custom_completers.add_re('ex*', complete_example)
282 _, matches = ip.complete('ex')
282 _, matches = ip.complete('ex')
283 assert matches.index('example2') < matches.index('example1')
283 assert matches.index('example2') < matches.index('example1')
284
284
285 def test_unicode_completions(self):
285 def test_unicode_completions(self):
286 ip = get_ipython()
286 ip = get_ipython()
287 # Some strings that trigger different types of completion. Check them both
287 # Some strings that trigger different types of completion. Check them both
288 # in str and unicode forms
288 # in str and unicode forms
289 s = ["ru", "%ru", "cd /", "floa", "float(x)/"]
289 s = ["ru", "%ru", "cd /", "floa", "float(x)/"]
290 for t in s + list(map(str, s)):
290 for t in s + list(map(str, s)):
291 # We don't need to check exact completion values (they may change
291 # We don't need to check exact completion values (they may change
292 # depending on the state of the namespace, but at least no exceptions
292 # depending on the state of the namespace, but at least no exceptions
293 # should be thrown and the return value should be a pair of text, list
293 # should be thrown and the return value should be a pair of text, list
294 # values.
294 # values.
295 text, matches = ip.complete(t)
295 text, matches = ip.complete(t)
296 self.assertIsInstance(text, str)
296 self.assertIsInstance(text, str)
297 self.assertIsInstance(matches, list)
297 self.assertIsInstance(matches, list)
298
298
299 def test_latex_completions(self):
299 def test_latex_completions(self):
300 from IPython.core.latex_symbols import latex_symbols
300 from IPython.core.latex_symbols import latex_symbols
301 import random
301 import random
302
302
303 ip = get_ipython()
303 ip = get_ipython()
304 # Test some random unicode symbols
304 # Test some random unicode symbols
305 keys = random.sample(sorted(latex_symbols), 10)
305 keys = random.sample(sorted(latex_symbols), 10)
306 for k in keys:
306 for k in keys:
307 text, matches = ip.complete(k)
307 text, matches = ip.complete(k)
308 self.assertEqual(text, k)
308 self.assertEqual(text, k)
309 self.assertEqual(matches, [latex_symbols[k]])
309 self.assertEqual(matches, [latex_symbols[k]])
310 # Test a more complex line
310 # Test a more complex line
311 text, matches = ip.complete("print(\\alpha")
311 text, matches = ip.complete("print(\\alpha")
312 self.assertEqual(text, "\\alpha")
312 self.assertEqual(text, "\\alpha")
313 self.assertEqual(matches[0], latex_symbols["\\alpha"])
313 self.assertEqual(matches[0], latex_symbols["\\alpha"])
314 # Test multiple matching latex symbols
314 # Test multiple matching latex symbols
315 text, matches = ip.complete("\\al")
315 text, matches = ip.complete("\\al")
316 self.assertIn("\\alpha", matches)
316 self.assertIn("\\alpha", matches)
317 self.assertIn("\\aleph", matches)
317 self.assertIn("\\aleph", matches)
318
318
319 def test_latex_no_results(self):
319 def test_latex_no_results(self):
320 """
320 """
321 forward latex should really return nothing in either field if nothing is found.
321 forward latex should really return nothing in either field if nothing is found.
322 """
322 """
323 ip = get_ipython()
323 ip = get_ipython()
324 text, matches = ip.Completer.latex_matches("\\really_i_should_match_nothing")
324 text, matches = ip.Completer.latex_matches("\\really_i_should_match_nothing")
325 self.assertEqual(text, "")
325 self.assertEqual(text, "")
326 self.assertEqual(matches, ())
326 self.assertEqual(matches, ())
327
327
328 def test_back_latex_completion(self):
328 def test_back_latex_completion(self):
329 ip = get_ipython()
329 ip = get_ipython()
330
330
331 # do not return more than 1 matches for \beta, only the latex one.
331 # do not return more than 1 matches for \beta, only the latex one.
332 name, matches = ip.complete("\\Ξ²")
332 name, matches = ip.complete("\\Ξ²")
333 self.assertEqual(matches, ["\\beta"])
333 self.assertEqual(matches, ["\\beta"])
334
334
335 def test_back_unicode_completion(self):
335 def test_back_unicode_completion(self):
336 ip = get_ipython()
336 ip = get_ipython()
337
337
338 name, matches = ip.complete("\\β…€")
338 name, matches = ip.complete("\\β…€")
339 self.assertEqual(matches, ["\\ROMAN NUMERAL FIVE"])
339 self.assertEqual(matches, ["\\ROMAN NUMERAL FIVE"])
340
340
341 def test_forward_unicode_completion(self):
341 def test_forward_unicode_completion(self):
342 ip = get_ipython()
342 ip = get_ipython()
343
343
344 name, matches = ip.complete("\\ROMAN NUMERAL FIVE")
344 name, matches = ip.complete("\\ROMAN NUMERAL FIVE")
345 self.assertEqual(matches, ["β…€"]) # This is not a V
345 self.assertEqual(matches, ["β…€"]) # This is not a V
346 self.assertEqual(matches, ["\u2164"]) # same as above but explicit.
346 self.assertEqual(matches, ["\u2164"]) # same as above but explicit.
347
347
348 def test_delim_setting(self):
348 def test_delim_setting(self):
349 sp = completer.CompletionSplitter()
349 sp = completer.CompletionSplitter()
350 sp.delims = " "
350 sp.delims = " "
351 self.assertEqual(sp.delims, " ")
351 self.assertEqual(sp.delims, " ")
352 self.assertEqual(sp._delim_expr, r"[\ ]")
352 self.assertEqual(sp._delim_expr, r"[\ ]")
353
353
354 def test_spaces(self):
354 def test_spaces(self):
355 """Test with only spaces as split chars."""
355 """Test with only spaces as split chars."""
356 sp = completer.CompletionSplitter()
356 sp = completer.CompletionSplitter()
357 sp.delims = " "
357 sp.delims = " "
358 t = [("foo", "", "foo"), ("run foo", "", "foo"), ("run foo", "bar", "foo")]
358 t = [("foo", "", "foo"), ("run foo", "", "foo"), ("run foo", "bar", "foo")]
359 check_line_split(sp, t)
359 check_line_split(sp, t)
360
360
361 def test_has_open_quotes1(self):
361 def test_has_open_quotes1(self):
362 for s in ["'", "'''", "'hi' '"]:
362 for s in ["'", "'''", "'hi' '"]:
363 self.assertEqual(completer.has_open_quotes(s), "'")
363 self.assertEqual(completer.has_open_quotes(s), "'")
364
364
365 def test_has_open_quotes2(self):
365 def test_has_open_quotes2(self):
366 for s in ['"', '"""', '"hi" "']:
366 for s in ['"', '"""', '"hi" "']:
367 self.assertEqual(completer.has_open_quotes(s), '"')
367 self.assertEqual(completer.has_open_quotes(s), '"')
368
368
369 def test_has_open_quotes3(self):
369 def test_has_open_quotes3(self):
370 for s in ["''", "''' '''", "'hi' 'ipython'"]:
370 for s in ["''", "''' '''", "'hi' 'ipython'"]:
371 self.assertFalse(completer.has_open_quotes(s))
371 self.assertFalse(completer.has_open_quotes(s))
372
372
373 def test_has_open_quotes4(self):
373 def test_has_open_quotes4(self):
374 for s in ['""', '""" """', '"hi" "ipython"']:
374 for s in ['""', '""" """', '"hi" "ipython"']:
375 self.assertFalse(completer.has_open_quotes(s))
375 self.assertFalse(completer.has_open_quotes(s))
376
376
377 @pytest.mark.xfail(
377 @pytest.mark.xfail(
378 sys.platform == "win32", reason="abspath completions fail on Windows"
378 sys.platform == "win32", reason="abspath completions fail on Windows"
379 )
379 )
380 def test_abspath_file_completions(self):
380 def test_abspath_file_completions(self):
381 ip = get_ipython()
381 ip = get_ipython()
382 with TemporaryDirectory() as tmpdir:
382 with TemporaryDirectory() as tmpdir:
383 prefix = os.path.join(tmpdir, "foo")
383 prefix = os.path.join(tmpdir, "foo")
384 suffixes = ["1", "2"]
384 suffixes = ["1", "2"]
385 names = [prefix + s for s in suffixes]
385 names = [prefix + s for s in suffixes]
386 for n in names:
386 for n in names:
387 open(n, "w", encoding="utf-8").close()
387 open(n, "w", encoding="utf-8").close()
388
388
389 # Check simple completion
389 # Check simple completion
390 c = ip.complete(prefix)[1]
390 c = ip.complete(prefix)[1]
391 self.assertEqual(c, names)
391 self.assertEqual(c, names)
392
392
393 # Now check with a function call
393 # Now check with a function call
394 cmd = 'a = f("%s' % prefix
394 cmd = 'a = f("%s' % prefix
395 c = ip.complete(prefix, cmd)[1]
395 c = ip.complete(prefix, cmd)[1]
396 comp = [prefix + s for s in suffixes]
396 comp = [prefix + s for s in suffixes]
397 self.assertEqual(c, comp)
397 self.assertEqual(c, comp)
398
398
399 def test_local_file_completions(self):
399 def test_local_file_completions(self):
400 ip = get_ipython()
400 ip = get_ipython()
401 with TemporaryWorkingDirectory():
401 with TemporaryWorkingDirectory():
402 prefix = "./foo"
402 prefix = "./foo"
403 suffixes = ["1", "2"]
403 suffixes = ["1", "2"]
404 names = [prefix + s for s in suffixes]
404 names = [prefix + s for s in suffixes]
405 for n in names:
405 for n in names:
406 open(n, "w", encoding="utf-8").close()
406 open(n, "w", encoding="utf-8").close()
407
407
408 # Check simple completion
408 # Check simple completion
409 c = ip.complete(prefix)[1]
409 c = ip.complete(prefix)[1]
410 self.assertEqual(c, names)
410 self.assertEqual(c, names)
411
411
412 # Now check with a function call
412 # Now check with a function call
413 cmd = 'a = f("%s' % prefix
413 cmd = 'a = f("%s' % prefix
414 c = ip.complete(prefix, cmd)[1]
414 c = ip.complete(prefix, cmd)[1]
415 comp = {prefix + s for s in suffixes}
415 comp = {prefix + s for s in suffixes}
416 self.assertTrue(comp.issubset(set(c)))
416 self.assertTrue(comp.issubset(set(c)))
417
417
418 def test_quoted_file_completions(self):
418 def test_quoted_file_completions(self):
419 ip = get_ipython()
419 ip = get_ipython()
420
420
421 def _(text):
421 def _(text):
422 return ip.Completer._complete(
422 return ip.Completer._complete(
423 cursor_line=0, cursor_pos=len(text), full_text=text
423 cursor_line=0, cursor_pos=len(text), full_text=text
424 )["IPCompleter.file_matcher"]["completions"]
424 )["IPCompleter.file_matcher"]["completions"]
425
425
426 with TemporaryWorkingDirectory():
426 with TemporaryWorkingDirectory():
427 name = "foo'bar"
427 name = "foo'bar"
428 open(name, "w", encoding="utf-8").close()
428 open(name, "w", encoding="utf-8").close()
429
429
430 # Don't escape Windows
430 # Don't escape Windows
431 escaped = name if sys.platform == "win32" else "foo\\'bar"
431 escaped = name if sys.platform == "win32" else "foo\\'bar"
432
432
433 # Single quote matches embedded single quote
433 # Single quote matches embedded single quote
434 c = _("open('foo")[0]
434 c = _("open('foo")[0]
435 self.assertEqual(c.text, escaped)
435 self.assertEqual(c.text, escaped)
436
436
437 # Double quote requires no escape
437 # Double quote requires no escape
438 c = _('open("foo')[0]
438 c = _('open("foo')[0]
439 self.assertEqual(c.text, name)
439 self.assertEqual(c.text, name)
440
440
441 # No quote requires an escape
441 # No quote requires an escape
442 c = _("%ls foo")[0]
442 c = _("%ls foo")[0]
443 self.assertEqual(c.text, escaped)
443 self.assertEqual(c.text, escaped)
444
444
445 @pytest.mark.xfail(
445 @pytest.mark.xfail(
446 sys.version_info.releaselevel in ("alpha",),
446 sys.version_info.releaselevel in ("alpha",),
447 reason="Parso does not yet parse 3.13",
447 reason="Parso does not yet parse 3.13",
448 )
448 )
449 def test_all_completions_dups(self):
449 def test_all_completions_dups(self):
450 """
450 """
451 Make sure the output of `IPCompleter.all_completions` does not have
451 Make sure the output of `IPCompleter.all_completions` does not have
452 duplicated prefixes.
452 duplicated prefixes.
453 """
453 """
454 ip = get_ipython()
454 ip = get_ipython()
455 c = ip.Completer
455 c = ip.Completer
456 ip.ex("class TestClass():\n\ta=1\n\ta1=2")
456 ip.ex("class TestClass():\n\ta=1\n\ta1=2")
457 for jedi_status in [True, False]:
457 for jedi_status in [True, False]:
458 with provisionalcompleter():
458 with provisionalcompleter():
459 ip.Completer.use_jedi = jedi_status
459 ip.Completer.use_jedi = jedi_status
460 matches = c.all_completions("TestCl")
460 matches = c.all_completions("TestCl")
461 assert matches == ["TestClass"], (jedi_status, matches)
461 assert matches == ["TestClass"], (jedi_status, matches)
462 matches = c.all_completions("TestClass.")
462 matches = c.all_completions("TestClass.")
463 assert len(matches) > 2, (jedi_status, matches)
463 assert len(matches) > 2, (jedi_status, matches)
464 matches = c.all_completions("TestClass.a")
464 matches = c.all_completions("TestClass.a")
465 if jedi_status:
465 if jedi_status:
466 assert matches == ["TestClass.a", "TestClass.a1"], jedi_status
466 assert matches == ["TestClass.a", "TestClass.a1"], jedi_status
467 else:
467 else:
468 assert matches == [".a", ".a1"], jedi_status
468 assert matches == [".a", ".a1"], jedi_status
469
469
470 @pytest.mark.xfail(
470 @pytest.mark.xfail(
471 sys.version_info.releaselevel in ("alpha",),
471 sys.version_info.releaselevel in ("alpha",),
472 reason="Parso does not yet parse 3.13",
472 reason="Parso does not yet parse 3.13",
473 )
473 )
474 def test_jedi(self):
474 def test_jedi(self):
475 """
475 """
476 A couple of issue we had with Jedi
476 A couple of issue we had with Jedi
477 """
477 """
478 ip = get_ipython()
478 ip = get_ipython()
479
479
480 def _test_complete(reason, s, comp, start=None, end=None):
480 def _test_complete(reason, s, comp, start=None, end=None):
481 l = len(s)
481 l = len(s)
482 start = start if start is not None else l
482 start = start if start is not None else l
483 end = end if end is not None else l
483 end = end if end is not None else l
484 with provisionalcompleter():
484 with provisionalcompleter():
485 ip.Completer.use_jedi = True
485 ip.Completer.use_jedi = True
486 completions = set(ip.Completer.completions(s, l))
486 completions = set(ip.Completer.completions(s, l))
487 ip.Completer.use_jedi = False
487 ip.Completer.use_jedi = False
488 assert Completion(start, end, comp) in completions, reason
488 assert Completion(start, end, comp) in completions, reason
489
489
490 def _test_not_complete(reason, s, comp):
490 def _test_not_complete(reason, s, comp):
491 l = len(s)
491 l = len(s)
492 with provisionalcompleter():
492 with provisionalcompleter():
493 ip.Completer.use_jedi = True
493 ip.Completer.use_jedi = True
494 completions = set(ip.Completer.completions(s, l))
494 completions = set(ip.Completer.completions(s, l))
495 ip.Completer.use_jedi = False
495 ip.Completer.use_jedi = False
496 assert Completion(l, l, comp) not in completions, reason
496 assert Completion(l, l, comp) not in completions, reason
497
497
498 import jedi
498 import jedi
499
499
500 jedi_version = tuple(int(i) for i in jedi.__version__.split(".")[:3])
500 jedi_version = tuple(int(i) for i in jedi.__version__.split(".")[:3])
501 if jedi_version > (0, 10):
501 if jedi_version > (0, 10):
502 _test_complete("jedi >0.9 should complete and not crash", "a=1;a.", "real")
502 _test_complete("jedi >0.9 should complete and not crash", "a=1;a.", "real")
503 _test_complete("can infer first argument", 'a=(1,"foo");a[0].', "real")
503 _test_complete("can infer first argument", 'a=(1,"foo");a[0].', "real")
504 _test_complete("can infer second argument", 'a=(1,"foo");a[1].', "capitalize")
504 _test_complete("can infer second argument", 'a=(1,"foo");a[1].', "capitalize")
505 _test_complete("cover duplicate completions", "im", "import", 0, 2)
505 _test_complete("cover duplicate completions", "im", "import", 0, 2)
506
506
507 _test_not_complete("does not mix types", 'a=(1,"foo");a[0].', "capitalize")
507 _test_not_complete("does not mix types", 'a=(1,"foo");a[0].', "capitalize")
508
508
509 @pytest.mark.xfail(
509 @pytest.mark.xfail(
510 sys.version_info.releaselevel in ("alpha",),
510 sys.version_info.releaselevel in ("alpha",),
511 reason="Parso does not yet parse 3.13",
511 reason="Parso does not yet parse 3.13",
512 )
512 )
513 def test_completion_have_signature(self):
513 def test_completion_have_signature(self):
514 """
514 """
515 Lets make sure jedi is capable of pulling out the signature of the function we are completing.
515 Lets make sure jedi is capable of pulling out the signature of the function we are completing.
516 """
516 """
517 ip = get_ipython()
517 ip = get_ipython()
518 with provisionalcompleter():
518 with provisionalcompleter():
519 ip.Completer.use_jedi = True
519 ip.Completer.use_jedi = True
520 completions = ip.Completer.completions("ope", 3)
520 completions = ip.Completer.completions("ope", 3)
521 c = next(completions) # should be `open`
521 c = next(completions) # should be `open`
522 ip.Completer.use_jedi = False
522 ip.Completer.use_jedi = False
523 assert "file" in c.signature, "Signature of function was not found by completer"
523 assert "file" in c.signature, "Signature of function was not found by completer"
524 assert (
524 assert (
525 "encoding" in c.signature
525 "encoding" in c.signature
526 ), "Signature of function was not found by completer"
526 ), "Signature of function was not found by completer"
527
527
528 @pytest.mark.xfail(
528 @pytest.mark.xfail(
529 sys.version_info.releaselevel in ("alpha",),
529 sys.version_info.releaselevel in ("alpha",),
530 reason="Parso does not yet parse 3.13",
530 reason="Parso does not yet parse 3.13",
531 )
531 )
532 def test_completions_have_type(self):
532 def test_completions_have_type(self):
533 """
533 """
534 Lets make sure matchers provide completion type.
534 Lets make sure matchers provide completion type.
535 """
535 """
536 ip = get_ipython()
536 ip = get_ipython()
537 with provisionalcompleter():
537 with provisionalcompleter():
538 ip.Completer.use_jedi = False
538 ip.Completer.use_jedi = False
539 completions = ip.Completer.completions("%tim", 3)
539 completions = ip.Completer.completions("%tim", 3)
540 c = next(completions) # should be `%time` or similar
540 c = next(completions) # should be `%time` or similar
541 assert c.type == "magic", "Type of magic was not assigned by completer"
541 assert c.type == "magic", "Type of magic was not assigned by completer"
542
542
543 @pytest.mark.xfail(
543 @pytest.mark.xfail(
544 parse(version("jedi")) <= parse("0.18.0"),
544 parse(version("jedi")) <= parse("0.18.0"),
545 reason="Known failure on jedi<=0.18.0",
545 reason="Known failure on jedi<=0.18.0",
546 strict=True,
546 strict=True,
547 )
547 )
548 def test_deduplicate_completions(self):
548 def test_deduplicate_completions(self):
549 """
549 """
550 Test that completions are correctly deduplicated (even if ranges are not the same)
550 Test that completions are correctly deduplicated (even if ranges are not the same)
551 """
551 """
552 ip = get_ipython()
552 ip = get_ipython()
553 ip.ex(
553 ip.ex(
554 textwrap.dedent(
554 textwrap.dedent(
555 """
555 """
556 class Z:
556 class Z:
557 zoo = 1
557 zoo = 1
558 """
558 """
559 )
559 )
560 )
560 )
561 with provisionalcompleter():
561 with provisionalcompleter():
562 ip.Completer.use_jedi = True
562 ip.Completer.use_jedi = True
563 l = list(
563 l = list(
564 _deduplicate_completions("Z.z", ip.Completer.completions("Z.z", 3))
564 _deduplicate_completions("Z.z", ip.Completer.completions("Z.z", 3))
565 )
565 )
566 ip.Completer.use_jedi = False
566 ip.Completer.use_jedi = False
567
567
568 assert len(l) == 1, "Completions (Z.z<tab>) correctly deduplicate: %s " % l
568 assert len(l) == 1, "Completions (Z.z<tab>) correctly deduplicate: %s " % l
569 assert l[0].text == "zoo" # and not `it.accumulate`
569 assert l[0].text == "zoo" # and not `it.accumulate`
570
570
571 @pytest.mark.xfail(
571 @pytest.mark.xfail(
572 sys.version_info.releaselevel in ("alpha",),
572 sys.version_info.releaselevel in ("alpha",),
573 reason="Parso does not yet parse 3.13",
573 reason="Parso does not yet parse 3.13",
574 )
574 )
575 def test_greedy_completions(self):
575 def test_greedy_completions(self):
576 """
576 """
577 Test the capability of the Greedy completer.
577 Test the capability of the Greedy completer.
578
578
579 Most of the test here does not really show off the greedy completer, for proof
579 Most of the test here does not really show off the greedy completer, for proof
580 each of the text below now pass with Jedi. The greedy completer is capable of more.
580 each of the text below now pass with Jedi. The greedy completer is capable of more.
581
581
582 See the :any:`test_dict_key_completion_contexts`
582 See the :any:`test_dict_key_completion_contexts`
583
583
584 """
584 """
585 ip = get_ipython()
585 ip = get_ipython()
586 ip.ex("a=list(range(5))")
586 ip.ex("a=list(range(5))")
587 ip.ex("d = {'a b': str}")
587 ip.ex("d = {'a b': str}")
588 _, c = ip.complete(".", line="a[0].")
588 _, c = ip.complete(".", line="a[0].")
589 self.assertFalse(".real" in c, "Shouldn't have completed on a[0]: %s" % c)
589 self.assertFalse(".real" in c, "Shouldn't have completed on a[0]: %s" % c)
590
590
591 def _(line, cursor_pos, expect, message, completion):
591 def _(line, cursor_pos, expect, message, completion):
592 with greedy_completion(), provisionalcompleter():
592 with greedy_completion(), provisionalcompleter():
593 ip.Completer.use_jedi = False
593 ip.Completer.use_jedi = False
594 _, c = ip.complete(".", line=line, cursor_pos=cursor_pos)
594 _, c = ip.complete(".", line=line, cursor_pos=cursor_pos)
595 self.assertIn(expect, c, message % c)
595 self.assertIn(expect, c, message % c)
596
596
597 ip.Completer.use_jedi = True
597 ip.Completer.use_jedi = True
598 with provisionalcompleter():
598 with provisionalcompleter():
599 completions = ip.Completer.completions(line, cursor_pos)
599 completions = ip.Completer.completions(line, cursor_pos)
600 self.assertIn(completion, list(completions))
600 self.assertIn(completion, list(completions))
601
601
602 with provisionalcompleter():
602 with provisionalcompleter():
603 _(
603 _(
604 "a[0].",
604 "a[0].",
605 5,
605 5,
606 ".real",
606 ".real",
607 "Should have completed on a[0].: %s",
607 "Should have completed on a[0].: %s",
608 Completion(5, 5, "real"),
608 Completion(5, 5, "real"),
609 )
609 )
610 _(
610 _(
611 "a[0].r",
611 "a[0].r",
612 6,
612 6,
613 ".real",
613 ".real",
614 "Should have completed on a[0].r: %s",
614 "Should have completed on a[0].r: %s",
615 Completion(5, 6, "real"),
615 Completion(5, 6, "real"),
616 )
616 )
617
617
618 _(
618 _(
619 "a[0].from_",
619 "a[0].from_",
620 10,
620 10,
621 ".from_bytes",
621 ".from_bytes",
622 "Should have completed on a[0].from_: %s",
622 "Should have completed on a[0].from_: %s",
623 Completion(5, 10, "from_bytes"),
623 Completion(5, 10, "from_bytes"),
624 )
624 )
625 _(
625 _(
626 "assert str.star",
626 "assert str.star",
627 14,
627 14,
628 ".startswith",
628 ".startswith",
629 "Should have completed on `assert str.star`: %s",
629 "Should have completed on `assert str.star`: %s",
630 Completion(11, 14, "startswith"),
630 Completion(11, 14, "startswith"),
631 )
631 )
632 _(
632 _(
633 "d['a b'].str",
633 "d['a b'].str",
634 12,
634 12,
635 ".strip",
635 ".strip",
636 "Should have completed on `d['a b'].str`: %s",
636 "Should have completed on `d['a b'].str`: %s",
637 Completion(9, 12, "strip"),
637 Completion(9, 12, "strip"),
638 )
638 )
639 _(
639 _(
640 "a.app",
640 "a.app",
641 4,
641 4,
642 ".append",
642 ".append",
643 "Should have completed on `a.app`: %s",
643 "Should have completed on `a.app`: %s",
644 Completion(2, 4, "append"),
644 Completion(2, 4, "append"),
645 )
645 )
646
646
647 def test_omit__names(self):
647 def test_omit__names(self):
648 # also happens to test IPCompleter as a configurable
648 # also happens to test IPCompleter as a configurable
649 ip = get_ipython()
649 ip = get_ipython()
650 ip._hidden_attr = 1
650 ip._hidden_attr = 1
651 ip._x = {}
651 ip._x = {}
652 c = ip.Completer
652 c = ip.Completer
653 ip.ex("ip=get_ipython()")
653 ip.ex("ip=get_ipython()")
654 cfg = Config()
654 cfg = Config()
655 cfg.IPCompleter.omit__names = 0
655 cfg.IPCompleter.omit__names = 0
656 c.update_config(cfg)
656 c.update_config(cfg)
657 with provisionalcompleter():
657 with provisionalcompleter():
658 c.use_jedi = False
658 c.use_jedi = False
659 s, matches = c.complete("ip.")
659 s, matches = c.complete("ip.")
660 self.assertIn(".__str__", matches)
660 self.assertIn(".__str__", matches)
661 self.assertIn("._hidden_attr", matches)
661 self.assertIn("._hidden_attr", matches)
662
662
663 # c.use_jedi = True
663 # c.use_jedi = True
664 # completions = set(c.completions('ip.', 3))
664 # completions = set(c.completions('ip.', 3))
665 # self.assertIn(Completion(3, 3, '__str__'), completions)
665 # self.assertIn(Completion(3, 3, '__str__'), completions)
666 # self.assertIn(Completion(3,3, "_hidden_attr"), completions)
666 # self.assertIn(Completion(3,3, "_hidden_attr"), completions)
667
667
668 cfg = Config()
668 cfg = Config()
669 cfg.IPCompleter.omit__names = 1
669 cfg.IPCompleter.omit__names = 1
670 c.update_config(cfg)
670 c.update_config(cfg)
671 with provisionalcompleter():
671 with provisionalcompleter():
672 c.use_jedi = False
672 c.use_jedi = False
673 s, matches = c.complete("ip.")
673 s, matches = c.complete("ip.")
674 self.assertNotIn(".__str__", matches)
674 self.assertNotIn(".__str__", matches)
675 # self.assertIn('ip._hidden_attr', matches)
675 # self.assertIn('ip._hidden_attr', matches)
676
676
677 # c.use_jedi = True
677 # c.use_jedi = True
678 # completions = set(c.completions('ip.', 3))
678 # completions = set(c.completions('ip.', 3))
679 # self.assertNotIn(Completion(3,3,'__str__'), completions)
679 # self.assertNotIn(Completion(3,3,'__str__'), completions)
680 # self.assertIn(Completion(3,3, "_hidden_attr"), completions)
680 # self.assertIn(Completion(3,3, "_hidden_attr"), completions)
681
681
682 cfg = Config()
682 cfg = Config()
683 cfg.IPCompleter.omit__names = 2
683 cfg.IPCompleter.omit__names = 2
684 c.update_config(cfg)
684 c.update_config(cfg)
685 with provisionalcompleter():
685 with provisionalcompleter():
686 c.use_jedi = False
686 c.use_jedi = False
687 s, matches = c.complete("ip.")
687 s, matches = c.complete("ip.")
688 self.assertNotIn(".__str__", matches)
688 self.assertNotIn(".__str__", matches)
689 self.assertNotIn("._hidden_attr", matches)
689 self.assertNotIn("._hidden_attr", matches)
690
690
691 # c.use_jedi = True
691 # c.use_jedi = True
692 # completions = set(c.completions('ip.', 3))
692 # completions = set(c.completions('ip.', 3))
693 # self.assertNotIn(Completion(3,3,'__str__'), completions)
693 # self.assertNotIn(Completion(3,3,'__str__'), completions)
694 # self.assertNotIn(Completion(3,3, "_hidden_attr"), completions)
694 # self.assertNotIn(Completion(3,3, "_hidden_attr"), completions)
695
695
696 with provisionalcompleter():
696 with provisionalcompleter():
697 c.use_jedi = False
697 c.use_jedi = False
698 s, matches = c.complete("ip._x.")
698 s, matches = c.complete("ip._x.")
699 self.assertIn(".keys", matches)
699 self.assertIn(".keys", matches)
700
700
701 # c.use_jedi = True
701 # c.use_jedi = True
702 # completions = set(c.completions('ip._x.', 6))
702 # completions = set(c.completions('ip._x.', 6))
703 # self.assertIn(Completion(6,6, "keys"), completions)
703 # self.assertIn(Completion(6,6, "keys"), completions)
704
704
705 del ip._hidden_attr
705 del ip._hidden_attr
706 del ip._x
706 del ip._x
707
707
708 def test_limit_to__all__False_ok(self):
708 def test_limit_to__all__False_ok(self):
709 """
709 """
710 Limit to all is deprecated, once we remove it this test can go away.
710 Limit to all is deprecated, once we remove it this test can go away.
711 """
711 """
712 ip = get_ipython()
712 ip = get_ipython()
713 c = ip.Completer
713 c = ip.Completer
714 c.use_jedi = False
714 c.use_jedi = False
715 ip.ex("class D: x=24")
715 ip.ex("class D: x=24")
716 ip.ex("d=D()")
716 ip.ex("d=D()")
717 cfg = Config()
717 cfg = Config()
718 cfg.IPCompleter.limit_to__all__ = False
718 cfg.IPCompleter.limit_to__all__ = False
719 c.update_config(cfg)
719 c.update_config(cfg)
720 s, matches = c.complete("d.")
720 s, matches = c.complete("d.")
721 self.assertIn(".x", matches)
721 self.assertIn(".x", matches)
722
722
723 def test_get__all__entries_ok(self):
723 def test_get__all__entries_ok(self):
724 class A:
724 class A:
725 __all__ = ["x", 1]
725 __all__ = ["x", 1]
726
726
727 words = completer.get__all__entries(A())
727 words = completer.get__all__entries(A())
728 self.assertEqual(words, ["x"])
728 self.assertEqual(words, ["x"])
729
729
730 def test_get__all__entries_no__all__ok(self):
730 def test_get__all__entries_no__all__ok(self):
731 class A:
731 class A:
732 pass
732 pass
733
733
734 words = completer.get__all__entries(A())
734 words = completer.get__all__entries(A())
735 self.assertEqual(words, [])
735 self.assertEqual(words, [])
736
736
737 def test_func_kw_completions(self):
737 def test_func_kw_completions(self):
738 ip = get_ipython()
738 ip = get_ipython()
739 c = ip.Completer
739 c = ip.Completer
740 c.use_jedi = False
740 c.use_jedi = False
741 ip.ex("def myfunc(a=1,b=2): return a+b")
741 ip.ex("def myfunc(a=1,b=2): return a+b")
742 s, matches = c.complete(None, "myfunc(1,b")
742 s, matches = c.complete(None, "myfunc(1,b")
743 self.assertIn("b=", matches)
743 self.assertIn("b=", matches)
744 # Simulate completing with cursor right after b (pos==10):
744 # Simulate completing with cursor right after b (pos==10):
745 s, matches = c.complete(None, "myfunc(1,b)", 10)
745 s, matches = c.complete(None, "myfunc(1,b)", 10)
746 self.assertIn("b=", matches)
746 self.assertIn("b=", matches)
747 s, matches = c.complete(None, 'myfunc(a="escaped\\")string",b')
747 s, matches = c.complete(None, 'myfunc(a="escaped\\")string",b')
748 self.assertIn("b=", matches)
748 self.assertIn("b=", matches)
749 # builtin function
749 # builtin function
750 s, matches = c.complete(None, "min(k, k")
750 s, matches = c.complete(None, "min(k, k")
751 self.assertIn("key=", matches)
751 self.assertIn("key=", matches)
752
752
753 def test_default_arguments_from_docstring(self):
753 def test_default_arguments_from_docstring(self):
754 ip = get_ipython()
754 ip = get_ipython()
755 c = ip.Completer
755 c = ip.Completer
756 kwd = c._default_arguments_from_docstring("min(iterable[, key=func]) -> value")
756 kwd = c._default_arguments_from_docstring("min(iterable[, key=func]) -> value")
757 self.assertEqual(kwd, ["key"])
757 self.assertEqual(kwd, ["key"])
758 # with cython type etc
758 # with cython type etc
759 kwd = c._default_arguments_from_docstring(
759 kwd = c._default_arguments_from_docstring(
760 "Minuit.migrad(self, int ncall=10000, resume=True, int nsplit=1)\n"
760 "Minuit.migrad(self, int ncall=10000, resume=True, int nsplit=1)\n"
761 )
761 )
762 self.assertEqual(kwd, ["ncall", "resume", "nsplit"])
762 self.assertEqual(kwd, ["ncall", "resume", "nsplit"])
763 # white spaces
763 # white spaces
764 kwd = c._default_arguments_from_docstring(
764 kwd = c._default_arguments_from_docstring(
765 "\n Minuit.migrad(self, int ncall=10000, resume=True, int nsplit=1)\n"
765 "\n Minuit.migrad(self, int ncall=10000, resume=True, int nsplit=1)\n"
766 )
766 )
767 self.assertEqual(kwd, ["ncall", "resume", "nsplit"])
767 self.assertEqual(kwd, ["ncall", "resume", "nsplit"])
768
768
769 def test_line_magics(self):
769 def test_line_magics(self):
770 ip = get_ipython()
770 ip = get_ipython()
771 c = ip.Completer
771 c = ip.Completer
772 s, matches = c.complete(None, "lsmag")
772 s, matches = c.complete(None, "lsmag")
773 self.assertIn("%lsmagic", matches)
773 self.assertIn("%lsmagic", matches)
774 s, matches = c.complete(None, "%lsmag")
774 s, matches = c.complete(None, "%lsmag")
775 self.assertIn("%lsmagic", matches)
775 self.assertIn("%lsmagic", matches)
776
776
777 def test_cell_magics(self):
777 def test_cell_magics(self):
778 from IPython.core.magic import register_cell_magic
778 from IPython.core.magic import register_cell_magic
779
779
780 @register_cell_magic
780 @register_cell_magic
781 def _foo_cellm(line, cell):
781 def _foo_cellm(line, cell):
782 pass
782 pass
783
783
784 ip = get_ipython()
784 ip = get_ipython()
785 c = ip.Completer
785 c = ip.Completer
786
786
787 s, matches = c.complete(None, "_foo_ce")
787 s, matches = c.complete(None, "_foo_ce")
788 self.assertIn("%%_foo_cellm", matches)
788 self.assertIn("%%_foo_cellm", matches)
789 s, matches = c.complete(None, "%%_foo_ce")
789 s, matches = c.complete(None, "%%_foo_ce")
790 self.assertIn("%%_foo_cellm", matches)
790 self.assertIn("%%_foo_cellm", matches)
791
791
792 def test_line_cell_magics(self):
792 def test_line_cell_magics(self):
793 from IPython.core.magic import register_line_cell_magic
793 from IPython.core.magic import register_line_cell_magic
794
794
795 @register_line_cell_magic
795 @register_line_cell_magic
796 def _bar_cellm(line, cell):
796 def _bar_cellm(line, cell):
797 pass
797 pass
798
798
799 ip = get_ipython()
799 ip = get_ipython()
800 c = ip.Completer
800 c = ip.Completer
801
801
802 # The policy here is trickier, see comments in completion code. The
802 # The policy here is trickier, see comments in completion code. The
803 # returned values depend on whether the user passes %% or not explicitly,
803 # returned values depend on whether the user passes %% or not explicitly,
804 # and this will show a difference if the same name is both a line and cell
804 # and this will show a difference if the same name is both a line and cell
805 # magic.
805 # magic.
806 s, matches = c.complete(None, "_bar_ce")
806 s, matches = c.complete(None, "_bar_ce")
807 self.assertIn("%_bar_cellm", matches)
807 self.assertIn("%_bar_cellm", matches)
808 self.assertIn("%%_bar_cellm", matches)
808 self.assertIn("%%_bar_cellm", matches)
809 s, matches = c.complete(None, "%_bar_ce")
809 s, matches = c.complete(None, "%_bar_ce")
810 self.assertIn("%_bar_cellm", matches)
810 self.assertIn("%_bar_cellm", matches)
811 self.assertIn("%%_bar_cellm", matches)
811 self.assertIn("%%_bar_cellm", matches)
812 s, matches = c.complete(None, "%%_bar_ce")
812 s, matches = c.complete(None, "%%_bar_ce")
813 self.assertNotIn("%_bar_cellm", matches)
813 self.assertNotIn("%_bar_cellm", matches)
814 self.assertIn("%%_bar_cellm", matches)
814 self.assertIn("%%_bar_cellm", matches)
815
815
816 def test_magic_completion_order(self):
816 def test_magic_completion_order(self):
817 ip = get_ipython()
817 ip = get_ipython()
818 c = ip.Completer
818 c = ip.Completer
819
819
820 # Test ordering of line and cell magics.
820 # Test ordering of line and cell magics.
821 text, matches = c.complete("timeit")
821 text, matches = c.complete("timeit")
822 self.assertEqual(matches, ["%timeit", "%%timeit"])
822 self.assertEqual(matches, ["%timeit", "%%timeit"])
823
823
824 def test_magic_completion_shadowing(self):
824 def test_magic_completion_shadowing(self):
825 ip = get_ipython()
825 ip = get_ipython()
826 c = ip.Completer
826 c = ip.Completer
827 c.use_jedi = False
827 c.use_jedi = False
828
828
829 # Before importing matplotlib, %matplotlib magic should be the only option.
829 # Before importing matplotlib, %matplotlib magic should be the only option.
830 text, matches = c.complete("mat")
830 text, matches = c.complete("mat")
831 self.assertEqual(matches, ["%matplotlib"])
831 self.assertEqual(matches, ["%matplotlib"])
832
832
833 # The newly introduced name should shadow the magic.
833 # The newly introduced name should shadow the magic.
834 ip.run_cell("matplotlib = 1")
834 ip.run_cell("matplotlib = 1")
835 text, matches = c.complete("mat")
835 text, matches = c.complete("mat")
836 self.assertEqual(matches, ["matplotlib"])
836 self.assertEqual(matches, ["matplotlib"])
837
837
838 # After removing matplotlib from namespace, the magic should again be
838 # After removing matplotlib from namespace, the magic should again be
839 # the only option.
839 # the only option.
840 del ip.user_ns["matplotlib"]
840 del ip.user_ns["matplotlib"]
841 text, matches = c.complete("mat")
841 text, matches = c.complete("mat")
842 self.assertEqual(matches, ["%matplotlib"])
842 self.assertEqual(matches, ["%matplotlib"])
843
843
844 def test_magic_completion_shadowing_explicit(self):
844 def test_magic_completion_shadowing_explicit(self):
845 """
845 """
846 If the user try to complete a shadowed magic, and explicit % start should
846 If the user try to complete a shadowed magic, and explicit % start should
847 still return the completions.
847 still return the completions.
848 """
848 """
849 ip = get_ipython()
849 ip = get_ipython()
850 c = ip.Completer
850 c = ip.Completer
851
851
852 # Before importing matplotlib, %matplotlib magic should be the only option.
852 # Before importing matplotlib, %matplotlib magic should be the only option.
853 text, matches = c.complete("%mat")
853 text, matches = c.complete("%mat")
854 self.assertEqual(matches, ["%matplotlib"])
854 self.assertEqual(matches, ["%matplotlib"])
855
855
856 ip.run_cell("matplotlib = 1")
856 ip.run_cell("matplotlib = 1")
857
857
858 # After removing matplotlib from namespace, the magic should still be
858 # After removing matplotlib from namespace, the magic should still be
859 # the only option.
859 # the only option.
860 text, matches = c.complete("%mat")
860 text, matches = c.complete("%mat")
861 self.assertEqual(matches, ["%matplotlib"])
861 self.assertEqual(matches, ["%matplotlib"])
862
862
863 def test_magic_config(self):
863 def test_magic_config(self):
864 ip = get_ipython()
864 ip = get_ipython()
865 c = ip.Completer
865 c = ip.Completer
866
866
867 s, matches = c.complete(None, "conf")
867 s, matches = c.complete(None, "conf")
868 self.assertIn("%config", matches)
868 self.assertIn("%config", matches)
869 s, matches = c.complete(None, "conf")
869 s, matches = c.complete(None, "conf")
870 self.assertNotIn("AliasManager", matches)
870 self.assertNotIn("AliasManager", matches)
871 s, matches = c.complete(None, "config ")
871 s, matches = c.complete(None, "config ")
872 self.assertIn("AliasManager", matches)
872 self.assertIn("AliasManager", matches)
873 s, matches = c.complete(None, "%config ")
873 s, matches = c.complete(None, "%config ")
874 self.assertIn("AliasManager", matches)
874 self.assertIn("AliasManager", matches)
875 s, matches = c.complete(None, "config Ali")
875 s, matches = c.complete(None, "config Ali")
876 self.assertListEqual(["AliasManager"], matches)
876 self.assertListEqual(["AliasManager"], matches)
877 s, matches = c.complete(None, "%config Ali")
877 s, matches = c.complete(None, "%config Ali")
878 self.assertListEqual(["AliasManager"], matches)
878 self.assertListEqual(["AliasManager"], matches)
879 s, matches = c.complete(None, "config AliasManager")
879 s, matches = c.complete(None, "config AliasManager")
880 self.assertListEqual(["AliasManager"], matches)
880 self.assertListEqual(["AliasManager"], matches)
881 s, matches = c.complete(None, "%config AliasManager")
881 s, matches = c.complete(None, "%config AliasManager")
882 self.assertListEqual(["AliasManager"], matches)
882 self.assertListEqual(["AliasManager"], matches)
883 s, matches = c.complete(None, "config AliasManager.")
883 s, matches = c.complete(None, "config AliasManager.")
884 self.assertIn("AliasManager.default_aliases", matches)
884 self.assertIn("AliasManager.default_aliases", matches)
885 s, matches = c.complete(None, "%config AliasManager.")
885 s, matches = c.complete(None, "%config AliasManager.")
886 self.assertIn("AliasManager.default_aliases", matches)
886 self.assertIn("AliasManager.default_aliases", matches)
887 s, matches = c.complete(None, "config AliasManager.de")
887 s, matches = c.complete(None, "config AliasManager.de")
888 self.assertListEqual(["AliasManager.default_aliases"], matches)
888 self.assertListEqual(["AliasManager.default_aliases"], matches)
889 s, matches = c.complete(None, "config AliasManager.de")
889 s, matches = c.complete(None, "config AliasManager.de")
890 self.assertListEqual(["AliasManager.default_aliases"], matches)
890 self.assertListEqual(["AliasManager.default_aliases"], matches)
891
891
892 def test_magic_color(self):
892 def test_magic_color(self):
893 ip = get_ipython()
893 ip = get_ipython()
894 c = ip.Completer
894 c = ip.Completer
895
895
896 s, matches = c.complete(None, "colo")
896 s, matches = c.complete(None, "colo")
897 self.assertIn("%colors", matches)
897 self.assertIn("%colors", matches)
898 s, matches = c.complete(None, "colo")
898 s, matches = c.complete(None, "colo")
899 self.assertNotIn("NoColor", matches)
899 self.assertNotIn("NoColor", matches)
900 s, matches = c.complete(None, "%colors") # No trailing space
900 s, matches = c.complete(None, "%colors") # No trailing space
901 self.assertNotIn("NoColor", matches)
901 self.assertNotIn("NoColor", matches)
902 s, matches = c.complete(None, "colors ")
902 s, matches = c.complete(None, "colors ")
903 self.assertIn("NoColor", matches)
903 self.assertIn("NoColor", matches)
904 s, matches = c.complete(None, "%colors ")
904 s, matches = c.complete(None, "%colors ")
905 self.assertIn("NoColor", matches)
905 self.assertIn("NoColor", matches)
906 s, matches = c.complete(None, "colors NoCo")
906 s, matches = c.complete(None, "colors NoCo")
907 self.assertListEqual(["NoColor"], matches)
907 self.assertListEqual(["NoColor"], matches)
908 s, matches = c.complete(None, "%colors NoCo")
908 s, matches = c.complete(None, "%colors NoCo")
909 self.assertListEqual(["NoColor"], matches)
909 self.assertListEqual(["NoColor"], matches)
910
910
911 def test_match_dict_keys(self):
911 def test_match_dict_keys(self):
912 """
912 """
913 Test that match_dict_keys works on a couple of use case does return what
913 Test that match_dict_keys works on a couple of use case does return what
914 expected, and does not crash
914 expected, and does not crash
915 """
915 """
916 delims = " \t\n`!@#$^&*()=+[{]}\\|;:'\",<>?"
916 delims = " \t\n`!@#$^&*()=+[{]}\\|;:'\",<>?"
917
917
918 def match(*args, **kwargs):
918 def match(*args, **kwargs):
919 quote, offset, matches = match_dict_keys(*args, delims=delims, **kwargs)
919 quote, offset, matches = match_dict_keys(*args, delims=delims, **kwargs)
920 return quote, offset, list(matches)
920 return quote, offset, list(matches)
921
921
922 keys = ["foo", b"far"]
922 keys = ["foo", b"far"]
923 assert match(keys, "b'") == ("'", 2, ["far"])
923 assert match(keys, "b'") == ("'", 2, ["far"])
924 assert match(keys, "b'f") == ("'", 2, ["far"])
924 assert match(keys, "b'f") == ("'", 2, ["far"])
925 assert match(keys, 'b"') == ('"', 2, ["far"])
925 assert match(keys, 'b"') == ('"', 2, ["far"])
926 assert match(keys, 'b"f') == ('"', 2, ["far"])
926 assert match(keys, 'b"f') == ('"', 2, ["far"])
927
927
928 assert match(keys, "'") == ("'", 1, ["foo"])
928 assert match(keys, "'") == ("'", 1, ["foo"])
929 assert match(keys, "'f") == ("'", 1, ["foo"])
929 assert match(keys, "'f") == ("'", 1, ["foo"])
930 assert match(keys, '"') == ('"', 1, ["foo"])
930 assert match(keys, '"') == ('"', 1, ["foo"])
931 assert match(keys, '"f') == ('"', 1, ["foo"])
931 assert match(keys, '"f') == ('"', 1, ["foo"])
932
932
933 # Completion on first item of tuple
933 # Completion on first item of tuple
934 keys = [("foo", 1111), ("foo", 2222), (3333, "bar"), (3333, "test")]
934 keys = [("foo", 1111), ("foo", 2222), (3333, "bar"), (3333, "test")]
935 assert match(keys, "'f") == ("'", 1, ["foo"])
935 assert match(keys, "'f") == ("'", 1, ["foo"])
936 assert match(keys, "33") == ("", 0, ["3333"])
936 assert match(keys, "33") == ("", 0, ["3333"])
937
937
938 # Completion on numbers
938 # Completion on numbers
939 keys = [
939 keys = [
940 0xDEADBEEF,
940 0xDEADBEEF,
941 1111,
941 1111,
942 1234,
942 1234,
943 "1999",
943 "1999",
944 0b10101,
944 0b10101,
945 22,
945 22,
946 ] # 0xDEADBEEF = 3735928559; 0b10101 = 21
946 ] # 0xDEADBEEF = 3735928559; 0b10101 = 21
947 assert match(keys, "0xdead") == ("", 0, ["0xdeadbeef"])
947 assert match(keys, "0xdead") == ("", 0, ["0xdeadbeef"])
948 assert match(keys, "1") == ("", 0, ["1111", "1234"])
948 assert match(keys, "1") == ("", 0, ["1111", "1234"])
949 assert match(keys, "2") == ("", 0, ["21", "22"])
949 assert match(keys, "2") == ("", 0, ["21", "22"])
950 assert match(keys, "0b101") == ("", 0, ["0b10101", "0b10110"])
950 assert match(keys, "0b101") == ("", 0, ["0b10101", "0b10110"])
951
951
952 # Should yield on variables
952 # Should yield on variables
953 assert match(keys, "a_variable") == ("", 0, [])
953 assert match(keys, "a_variable") == ("", 0, [])
954
954
955 # Should pass over invalid literals
955 # Should pass over invalid literals
956 assert match(keys, "'' ''") == ("", 0, [])
956 assert match(keys, "'' ''") == ("", 0, [])
957
957
958 def test_match_dict_keys_tuple(self):
958 def test_match_dict_keys_tuple(self):
959 """
959 """
960 Test that match_dict_keys called with extra prefix works on a couple of use case,
960 Test that match_dict_keys called with extra prefix works on a couple of use case,
961 does return what expected, and does not crash.
961 does return what expected, and does not crash.
962 """
962 """
963 delims = " \t\n`!@#$^&*()=+[{]}\\|;:'\",<>?"
963 delims = " \t\n`!@#$^&*()=+[{]}\\|;:'\",<>?"
964
964
965 keys = [("foo", "bar"), ("foo", "oof"), ("foo", b"bar"), ('other', 'test')]
965 keys = [("foo", "bar"), ("foo", "oof"), ("foo", b"bar"), ('other', 'test')]
966
966
967 def match(*args, extra=None, **kwargs):
967 def match(*args, extra=None, **kwargs):
968 quote, offset, matches = match_dict_keys(
968 quote, offset, matches = match_dict_keys(
969 *args, delims=delims, extra_prefix=extra, **kwargs
969 *args, delims=delims, extra_prefix=extra, **kwargs
970 )
970 )
971 return quote, offset, list(matches)
971 return quote, offset, list(matches)
972
972
973 # Completion on first key == "foo"
973 # Completion on first key == "foo"
974 assert match(keys, "'", extra=("foo",)) == ("'", 1, ["bar", "oof"])
974 assert match(keys, "'", extra=("foo",)) == ("'", 1, ["bar", "oof"])
975 assert match(keys, '"', extra=("foo",)) == ('"', 1, ["bar", "oof"])
975 assert match(keys, '"', extra=("foo",)) == ('"', 1, ["bar", "oof"])
976 assert match(keys, "'o", extra=("foo",)) == ("'", 1, ["oof"])
976 assert match(keys, "'o", extra=("foo",)) == ("'", 1, ["oof"])
977 assert match(keys, '"o', extra=("foo",)) == ('"', 1, ["oof"])
977 assert match(keys, '"o', extra=("foo",)) == ('"', 1, ["oof"])
978 assert match(keys, "b'", extra=("foo",)) == ("'", 2, ["bar"])
978 assert match(keys, "b'", extra=("foo",)) == ("'", 2, ["bar"])
979 assert match(keys, 'b"', extra=("foo",)) == ('"', 2, ["bar"])
979 assert match(keys, 'b"', extra=("foo",)) == ('"', 2, ["bar"])
980 assert match(keys, "b'b", extra=("foo",)) == ("'", 2, ["bar"])
980 assert match(keys, "b'b", extra=("foo",)) == ("'", 2, ["bar"])
981 assert match(keys, 'b"b', extra=("foo",)) == ('"', 2, ["bar"])
981 assert match(keys, 'b"b', extra=("foo",)) == ('"', 2, ["bar"])
982
982
983 # No Completion
983 # No Completion
984 assert match(keys, "'", extra=("no_foo",)) == ("'", 1, [])
984 assert match(keys, "'", extra=("no_foo",)) == ("'", 1, [])
985 assert match(keys, "'", extra=("fo",)) == ("'", 1, [])
985 assert match(keys, "'", extra=("fo",)) == ("'", 1, [])
986
986
987 keys = [("foo1", "foo2", "foo3", "foo4"), ("foo1", "foo2", "bar", "foo4")]
987 keys = [("foo1", "foo2", "foo3", "foo4"), ("foo1", "foo2", "bar", "foo4")]
988 assert match(keys, "'foo", extra=("foo1",)) == ("'", 1, ["foo2"])
988 assert match(keys, "'foo", extra=("foo1",)) == ("'", 1, ["foo2"])
989 assert match(keys, "'foo", extra=("foo1", "foo2")) == ("'", 1, ["foo3"])
989 assert match(keys, "'foo", extra=("foo1", "foo2")) == ("'", 1, ["foo3"])
990 assert match(keys, "'foo", extra=("foo1", "foo2", "foo3")) == ("'", 1, ["foo4"])
990 assert match(keys, "'foo", extra=("foo1", "foo2", "foo3")) == ("'", 1, ["foo4"])
991 assert match(keys, "'foo", extra=("foo1", "foo2", "foo3", "foo4")) == (
991 assert match(keys, "'foo", extra=("foo1", "foo2", "foo3", "foo4")) == (
992 "'",
992 "'",
993 1,
993 1,
994 [],
994 [],
995 )
995 )
996
996
997 keys = [("foo", 1111), ("foo", "2222"), (3333, "bar"), (3333, 4444)]
997 keys = [("foo", 1111), ("foo", "2222"), (3333, "bar"), (3333, 4444)]
998 assert match(keys, "'", extra=("foo",)) == ("'", 1, ["2222"])
998 assert match(keys, "'", extra=("foo",)) == ("'", 1, ["2222"])
999 assert match(keys, "", extra=("foo",)) == ("", 0, ["1111", "'2222'"])
999 assert match(keys, "", extra=("foo",)) == ("", 0, ["1111", "'2222'"])
1000 assert match(keys, "'", extra=(3333,)) == ("'", 1, ["bar"])
1000 assert match(keys, "'", extra=(3333,)) == ("'", 1, ["bar"])
1001 assert match(keys, "", extra=(3333,)) == ("", 0, ["'bar'", "4444"])
1001 assert match(keys, "", extra=(3333,)) == ("", 0, ["'bar'", "4444"])
1002 assert match(keys, "'", extra=("3333",)) == ("'", 1, [])
1002 assert match(keys, "'", extra=("3333",)) == ("'", 1, [])
1003 assert match(keys, "33") == ("", 0, ["3333"])
1003 assert match(keys, "33") == ("", 0, ["3333"])
1004
1004
1005 def test_dict_key_completion_closures(self):
1005 def test_dict_key_completion_closures(self):
1006 ip = get_ipython()
1006 ip = get_ipython()
1007 complete = ip.Completer.complete
1007 complete = ip.Completer.complete
1008 ip.Completer.auto_close_dict_keys = True
1008 ip.Completer.auto_close_dict_keys = True
1009
1009
1010 ip.user_ns["d"] = {
1010 ip.user_ns["d"] = {
1011 # tuple only
1011 # tuple only
1012 ("aa", 11): None,
1012 ("aa", 11): None,
1013 # tuple and non-tuple
1013 # tuple and non-tuple
1014 ("bb", 22): None,
1014 ("bb", 22): None,
1015 "bb": None,
1015 "bb": None,
1016 # non-tuple only
1016 # non-tuple only
1017 "cc": None,
1017 "cc": None,
1018 # numeric tuple only
1018 # numeric tuple only
1019 (77, "x"): None,
1019 (77, "x"): None,
1020 # numeric tuple and non-tuple
1020 # numeric tuple and non-tuple
1021 (88, "y"): None,
1021 (88, "y"): None,
1022 88: None,
1022 88: None,
1023 # numeric non-tuple only
1023 # numeric non-tuple only
1024 99: None,
1024 99: None,
1025 }
1025 }
1026
1026
1027 _, matches = complete(line_buffer="d[")
1027 _, matches = complete(line_buffer="d[")
1028 # should append `, ` if matches a tuple only
1028 # should append `, ` if matches a tuple only
1029 self.assertIn("'aa', ", matches)
1029 self.assertIn("'aa', ", matches)
1030 # should not append anything if matches a tuple and an item
1030 # should not append anything if matches a tuple and an item
1031 self.assertIn("'bb'", matches)
1031 self.assertIn("'bb'", matches)
1032 # should append `]` if matches and item only
1032 # should append `]` if matches and item only
1033 self.assertIn("'cc']", matches)
1033 self.assertIn("'cc']", matches)
1034
1034
1035 # should append `, ` if matches a tuple only
1035 # should append `, ` if matches a tuple only
1036 self.assertIn("77, ", matches)
1036 self.assertIn("77, ", matches)
1037 # should not append anything if matches a tuple and an item
1037 # should not append anything if matches a tuple and an item
1038 self.assertIn("88", matches)
1038 self.assertIn("88", matches)
1039 # should append `]` if matches and item only
1039 # should append `]` if matches and item only
1040 self.assertIn("99]", matches)
1040 self.assertIn("99]", matches)
1041
1041
1042 _, matches = complete(line_buffer="d['aa', ")
1042 _, matches = complete(line_buffer="d['aa', ")
1043 # should restrict matches to those matching tuple prefix
1043 # should restrict matches to those matching tuple prefix
1044 self.assertIn("11]", matches)
1044 self.assertIn("11]", matches)
1045 self.assertNotIn("'bb'", matches)
1045 self.assertNotIn("'bb'", matches)
1046 self.assertNotIn("'bb', ", matches)
1046 self.assertNotIn("'bb', ", matches)
1047 self.assertNotIn("'bb']", matches)
1047 self.assertNotIn("'bb']", matches)
1048 self.assertNotIn("'cc'", matches)
1048 self.assertNotIn("'cc'", matches)
1049 self.assertNotIn("'cc', ", matches)
1049 self.assertNotIn("'cc', ", matches)
1050 self.assertNotIn("'cc']", matches)
1050 self.assertNotIn("'cc']", matches)
1051 ip.Completer.auto_close_dict_keys = False
1051 ip.Completer.auto_close_dict_keys = False
1052
1052
1053 def test_dict_key_completion_string(self):
1053 def test_dict_key_completion_string(self):
1054 """Test dictionary key completion for string keys"""
1054 """Test dictionary key completion for string keys"""
1055 ip = get_ipython()
1055 ip = get_ipython()
1056 complete = ip.Completer.complete
1056 complete = ip.Completer.complete
1057
1057
1058 ip.user_ns["d"] = {"abc": None}
1058 ip.user_ns["d"] = {"abc": None}
1059
1059
1060 # check completion at different stages
1060 # check completion at different stages
1061 _, matches = complete(line_buffer="d[")
1061 _, matches = complete(line_buffer="d[")
1062 self.assertIn("'abc'", matches)
1062 self.assertIn("'abc'", matches)
1063 self.assertNotIn("'abc']", matches)
1063 self.assertNotIn("'abc']", matches)
1064
1064
1065 _, matches = complete(line_buffer="d['")
1065 _, matches = complete(line_buffer="d['")
1066 self.assertIn("abc", matches)
1066 self.assertIn("abc", matches)
1067 self.assertNotIn("abc']", matches)
1067 self.assertNotIn("abc']", matches)
1068
1068
1069 _, matches = complete(line_buffer="d['a")
1069 _, matches = complete(line_buffer="d['a")
1070 self.assertIn("abc", matches)
1070 self.assertIn("abc", matches)
1071 self.assertNotIn("abc']", matches)
1071 self.assertNotIn("abc']", matches)
1072
1072
1073 # check use of different quoting
1073 # check use of different quoting
1074 _, matches = complete(line_buffer='d["')
1074 _, matches = complete(line_buffer='d["')
1075 self.assertIn("abc", matches)
1075 self.assertIn("abc", matches)
1076 self.assertNotIn('abc"]', matches)
1076 self.assertNotIn('abc"]', matches)
1077
1077
1078 _, matches = complete(line_buffer='d["a')
1078 _, matches = complete(line_buffer='d["a')
1079 self.assertIn("abc", matches)
1079 self.assertIn("abc", matches)
1080 self.assertNotIn('abc"]', matches)
1080 self.assertNotIn('abc"]', matches)
1081
1081
1082 # check sensitivity to following context
1082 # check sensitivity to following context
1083 _, matches = complete(line_buffer="d[]", cursor_pos=2)
1083 _, matches = complete(line_buffer="d[]", cursor_pos=2)
1084 self.assertIn("'abc'", matches)
1084 self.assertIn("'abc'", matches)
1085
1085
1086 _, matches = complete(line_buffer="d['']", cursor_pos=3)
1086 _, matches = complete(line_buffer="d['']", cursor_pos=3)
1087 self.assertIn("abc", matches)
1087 self.assertIn("abc", matches)
1088 self.assertNotIn("abc'", matches)
1088 self.assertNotIn("abc'", matches)
1089 self.assertNotIn("abc']", matches)
1089 self.assertNotIn("abc']", matches)
1090
1090
1091 # check multiple solutions are correctly returned and that noise is not
1091 # check multiple solutions are correctly returned and that noise is not
1092 ip.user_ns["d"] = {
1092 ip.user_ns["d"] = {
1093 "abc": None,
1093 "abc": None,
1094 "abd": None,
1094 "abd": None,
1095 "bad": None,
1095 "bad": None,
1096 object(): None,
1096 object(): None,
1097 5: None,
1097 5: None,
1098 ("abe", None): None,
1098 ("abe", None): None,
1099 (None, "abf"): None
1099 (None, "abf"): None
1100 }
1100 }
1101
1101
1102 _, matches = complete(line_buffer="d['a")
1102 _, matches = complete(line_buffer="d['a")
1103 self.assertIn("abc", matches)
1103 self.assertIn("abc", matches)
1104 self.assertIn("abd", matches)
1104 self.assertIn("abd", matches)
1105 self.assertNotIn("bad", matches)
1105 self.assertNotIn("bad", matches)
1106 self.assertNotIn("abe", matches)
1106 self.assertNotIn("abe", matches)
1107 self.assertNotIn("abf", matches)
1107 self.assertNotIn("abf", matches)
1108 assert not any(m.endswith(("]", '"', "'")) for m in matches), matches
1108 assert not any(m.endswith(("]", '"', "'")) for m in matches), matches
1109
1109
1110 # check escaping and whitespace
1110 # check escaping and whitespace
1111 ip.user_ns["d"] = {"a\nb": None, "a'b": None, 'a"b': None, "a word": None}
1111 ip.user_ns["d"] = {"a\nb": None, "a'b": None, 'a"b': None, "a word": None}
1112 _, matches = complete(line_buffer="d['a")
1112 _, matches = complete(line_buffer="d['a")
1113 self.assertIn("a\\nb", matches)
1113 self.assertIn("a\\nb", matches)
1114 self.assertIn("a\\'b", matches)
1114 self.assertIn("a\\'b", matches)
1115 self.assertIn('a"b', matches)
1115 self.assertIn('a"b', matches)
1116 self.assertIn("a word", matches)
1116 self.assertIn("a word", matches)
1117 assert not any(m.endswith(("]", '"', "'")) for m in matches), matches
1117 assert not any(m.endswith(("]", '"', "'")) for m in matches), matches
1118
1118
1119 # - can complete on non-initial word of the string
1119 # - can complete on non-initial word of the string
1120 _, matches = complete(line_buffer="d['a w")
1120 _, matches = complete(line_buffer="d['a w")
1121 self.assertIn("word", matches)
1121 self.assertIn("word", matches)
1122
1122
1123 # - understands quote escaping
1123 # - understands quote escaping
1124 _, matches = complete(line_buffer="d['a\\'")
1124 _, matches = complete(line_buffer="d['a\\'")
1125 self.assertIn("b", matches)
1125 self.assertIn("b", matches)
1126
1126
1127 # - default quoting should work like repr
1127 # - default quoting should work like repr
1128 _, matches = complete(line_buffer="d[")
1128 _, matches = complete(line_buffer="d[")
1129 self.assertIn('"a\'b"', matches)
1129 self.assertIn('"a\'b"', matches)
1130
1130
1131 # - when opening quote with ", possible to match with unescaped apostrophe
1131 # - when opening quote with ", possible to match with unescaped apostrophe
1132 _, matches = complete(line_buffer="d[\"a'")
1132 _, matches = complete(line_buffer="d[\"a'")
1133 self.assertIn("b", matches)
1133 self.assertIn("b", matches)
1134
1134
1135 # need to not split at delims that readline won't split at
1135 # need to not split at delims that readline won't split at
1136 if "-" not in ip.Completer.splitter.delims:
1136 if "-" not in ip.Completer.splitter.delims:
1137 ip.user_ns["d"] = {"before-after": None}
1137 ip.user_ns["d"] = {"before-after": None}
1138 _, matches = complete(line_buffer="d['before-af")
1138 _, matches = complete(line_buffer="d['before-af")
1139 self.assertIn("before-after", matches)
1139 self.assertIn("before-after", matches)
1140
1140
1141 # check completion on tuple-of-string keys at different stage - on first key
1141 # check completion on tuple-of-string keys at different stage - on first key
1142 ip.user_ns["d"] = {('foo', 'bar'): None}
1142 ip.user_ns["d"] = {('foo', 'bar'): None}
1143 _, matches = complete(line_buffer="d[")
1143 _, matches = complete(line_buffer="d[")
1144 self.assertIn("'foo'", matches)
1144 self.assertIn("'foo'", matches)
1145 self.assertNotIn("'foo']", matches)
1145 self.assertNotIn("'foo']", matches)
1146 self.assertNotIn("'bar'", matches)
1146 self.assertNotIn("'bar'", matches)
1147 self.assertNotIn("foo", matches)
1147 self.assertNotIn("foo", matches)
1148 self.assertNotIn("bar", matches)
1148 self.assertNotIn("bar", matches)
1149
1149
1150 # - match the prefix
1150 # - match the prefix
1151 _, matches = complete(line_buffer="d['f")
1151 _, matches = complete(line_buffer="d['f")
1152 self.assertIn("foo", matches)
1152 self.assertIn("foo", matches)
1153 self.assertNotIn("foo']", matches)
1153 self.assertNotIn("foo']", matches)
1154 self.assertNotIn('foo"]', matches)
1154 self.assertNotIn('foo"]', matches)
1155 _, matches = complete(line_buffer="d['foo")
1155 _, matches = complete(line_buffer="d['foo")
1156 self.assertIn("foo", matches)
1156 self.assertIn("foo", matches)
1157
1157
1158 # - can complete on second key
1158 # - can complete on second key
1159 _, matches = complete(line_buffer="d['foo', ")
1159 _, matches = complete(line_buffer="d['foo', ")
1160 self.assertIn("'bar'", matches)
1160 self.assertIn("'bar'", matches)
1161 _, matches = complete(line_buffer="d['foo', 'b")
1161 _, matches = complete(line_buffer="d['foo', 'b")
1162 self.assertIn("bar", matches)
1162 self.assertIn("bar", matches)
1163 self.assertNotIn("foo", matches)
1163 self.assertNotIn("foo", matches)
1164
1164
1165 # - does not propose missing keys
1165 # - does not propose missing keys
1166 _, matches = complete(line_buffer="d['foo', 'f")
1166 _, matches = complete(line_buffer="d['foo', 'f")
1167 self.assertNotIn("bar", matches)
1167 self.assertNotIn("bar", matches)
1168 self.assertNotIn("foo", matches)
1168 self.assertNotIn("foo", matches)
1169
1169
1170 # check sensitivity to following context
1170 # check sensitivity to following context
1171 _, matches = complete(line_buffer="d['foo',]", cursor_pos=8)
1171 _, matches = complete(line_buffer="d['foo',]", cursor_pos=8)
1172 self.assertIn("'bar'", matches)
1172 self.assertIn("'bar'", matches)
1173 self.assertNotIn("bar", matches)
1173 self.assertNotIn("bar", matches)
1174 self.assertNotIn("'foo'", matches)
1174 self.assertNotIn("'foo'", matches)
1175 self.assertNotIn("foo", matches)
1175 self.assertNotIn("foo", matches)
1176
1176
1177 _, matches = complete(line_buffer="d['']", cursor_pos=3)
1177 _, matches = complete(line_buffer="d['']", cursor_pos=3)
1178 self.assertIn("foo", matches)
1178 self.assertIn("foo", matches)
1179 assert not any(m.endswith(("]", '"', "'")) for m in matches), matches
1179 assert not any(m.endswith(("]", '"', "'")) for m in matches), matches
1180
1180
1181 _, matches = complete(line_buffer='d[""]', cursor_pos=3)
1181 _, matches = complete(line_buffer='d[""]', cursor_pos=3)
1182 self.assertIn("foo", matches)
1182 self.assertIn("foo", matches)
1183 assert not any(m.endswith(("]", '"', "'")) for m in matches), matches
1183 assert not any(m.endswith(("]", '"', "'")) for m in matches), matches
1184
1184
1185 _, matches = complete(line_buffer='d["foo","]', cursor_pos=9)
1185 _, matches = complete(line_buffer='d["foo","]', cursor_pos=9)
1186 self.assertIn("bar", matches)
1186 self.assertIn("bar", matches)
1187 assert not any(m.endswith(("]", '"', "'")) for m in matches), matches
1187 assert not any(m.endswith(("]", '"', "'")) for m in matches), matches
1188
1188
1189 _, matches = complete(line_buffer='d["foo",]', cursor_pos=8)
1189 _, matches = complete(line_buffer='d["foo",]', cursor_pos=8)
1190 self.assertIn("'bar'", matches)
1190 self.assertIn("'bar'", matches)
1191 self.assertNotIn("bar", matches)
1191 self.assertNotIn("bar", matches)
1192
1192
1193 # Can complete with longer tuple keys
1193 # Can complete with longer tuple keys
1194 ip.user_ns["d"] = {('foo', 'bar', 'foobar'): None}
1194 ip.user_ns["d"] = {('foo', 'bar', 'foobar'): None}
1195
1195
1196 # - can complete second key
1196 # - can complete second key
1197 _, matches = complete(line_buffer="d['foo', 'b")
1197 _, matches = complete(line_buffer="d['foo', 'b")
1198 self.assertIn("bar", matches)
1198 self.assertIn("bar", matches)
1199 self.assertNotIn("foo", matches)
1199 self.assertNotIn("foo", matches)
1200 self.assertNotIn("foobar", matches)
1200 self.assertNotIn("foobar", matches)
1201
1201
1202 # - can complete third key
1202 # - can complete third key
1203 _, matches = complete(line_buffer="d['foo', 'bar', 'fo")
1203 _, matches = complete(line_buffer="d['foo', 'bar', 'fo")
1204 self.assertIn("foobar", matches)
1204 self.assertIn("foobar", matches)
1205 self.assertNotIn("foo", matches)
1205 self.assertNotIn("foo", matches)
1206 self.assertNotIn("bar", matches)
1206 self.assertNotIn("bar", matches)
1207
1207
1208 def test_dict_key_completion_numbers(self):
1208 def test_dict_key_completion_numbers(self):
1209 ip = get_ipython()
1209 ip = get_ipython()
1210 complete = ip.Completer.complete
1210 complete = ip.Completer.complete
1211
1211
1212 ip.user_ns["d"] = {
1212 ip.user_ns["d"] = {
1213 0xDEADBEEF: None, # 3735928559
1213 0xDEADBEEF: None, # 3735928559
1214 1111: None,
1214 1111: None,
1215 1234: None,
1215 1234: None,
1216 "1999": None,
1216 "1999": None,
1217 0b10101: None, # 21
1217 0b10101: None, # 21
1218 22: None,
1218 22: None,
1219 }
1219 }
1220 _, matches = complete(line_buffer="d[1")
1220 _, matches = complete(line_buffer="d[1")
1221 self.assertIn("1111", matches)
1221 self.assertIn("1111", matches)
1222 self.assertIn("1234", matches)
1222 self.assertIn("1234", matches)
1223 self.assertNotIn("1999", matches)
1223 self.assertNotIn("1999", matches)
1224 self.assertNotIn("'1999'", matches)
1224 self.assertNotIn("'1999'", matches)
1225
1225
1226 _, matches = complete(line_buffer="d[0xdead")
1226 _, matches = complete(line_buffer="d[0xdead")
1227 self.assertIn("0xdeadbeef", matches)
1227 self.assertIn("0xdeadbeef", matches)
1228
1228
1229 _, matches = complete(line_buffer="d[2")
1229 _, matches = complete(line_buffer="d[2")
1230 self.assertIn("21", matches)
1230 self.assertIn("21", matches)
1231 self.assertIn("22", matches)
1231 self.assertIn("22", matches)
1232
1232
1233 _, matches = complete(line_buffer="d[0b101")
1233 _, matches = complete(line_buffer="d[0b101")
1234 self.assertIn("0b10101", matches)
1234 self.assertIn("0b10101", matches)
1235 self.assertIn("0b10110", matches)
1235 self.assertIn("0b10110", matches)
1236
1236
1237 def test_dict_key_completion_contexts(self):
1237 def test_dict_key_completion_contexts(self):
1238 """Test expression contexts in which dict key completion occurs"""
1238 """Test expression contexts in which dict key completion occurs"""
1239 ip = get_ipython()
1239 ip = get_ipython()
1240 complete = ip.Completer.complete
1240 complete = ip.Completer.complete
1241 d = {"abc": None}
1241 d = {"abc": None}
1242 ip.user_ns["d"] = d
1242 ip.user_ns["d"] = d
1243
1243
1244 class C:
1244 class C:
1245 data = d
1245 data = d
1246
1246
1247 ip.user_ns["C"] = C
1247 ip.user_ns["C"] = C
1248 ip.user_ns["get"] = lambda: d
1248 ip.user_ns["get"] = lambda: d
1249 ip.user_ns["nested"] = {"x": d}
1249 ip.user_ns["nested"] = {"x": d}
1250
1250
1251 def assert_no_completion(**kwargs):
1251 def assert_no_completion(**kwargs):
1252 _, matches = complete(**kwargs)
1252 _, matches = complete(**kwargs)
1253 self.assertNotIn("abc", matches)
1253 self.assertNotIn("abc", matches)
1254 self.assertNotIn("abc'", matches)
1254 self.assertNotIn("abc'", matches)
1255 self.assertNotIn("abc']", matches)
1255 self.assertNotIn("abc']", matches)
1256 self.assertNotIn("'abc'", matches)
1256 self.assertNotIn("'abc'", matches)
1257 self.assertNotIn("'abc']", matches)
1257 self.assertNotIn("'abc']", matches)
1258
1258
1259 def assert_completion(**kwargs):
1259 def assert_completion(**kwargs):
1260 _, matches = complete(**kwargs)
1260 _, matches = complete(**kwargs)
1261 self.assertIn("'abc'", matches)
1261 self.assertIn("'abc'", matches)
1262 self.assertNotIn("'abc']", matches)
1262 self.assertNotIn("'abc']", matches)
1263
1263
1264 # no completion after string closed, even if reopened
1264 # no completion after string closed, even if reopened
1265 assert_no_completion(line_buffer="d['a'")
1265 assert_no_completion(line_buffer="d['a'")
1266 assert_no_completion(line_buffer='d["a"')
1266 assert_no_completion(line_buffer='d["a"')
1267 assert_no_completion(line_buffer="d['a' + ")
1267 assert_no_completion(line_buffer="d['a' + ")
1268 assert_no_completion(line_buffer="d['a' + '")
1268 assert_no_completion(line_buffer="d['a' + '")
1269
1269
1270 # completion in non-trivial expressions
1270 # completion in non-trivial expressions
1271 assert_completion(line_buffer="+ d[")
1271 assert_completion(line_buffer="+ d[")
1272 assert_completion(line_buffer="(d[")
1272 assert_completion(line_buffer="(d[")
1273 assert_completion(line_buffer="C.data[")
1273 assert_completion(line_buffer="C.data[")
1274
1274
1275 # nested dict completion
1275 # nested dict completion
1276 assert_completion(line_buffer="nested['x'][")
1276 assert_completion(line_buffer="nested['x'][")
1277
1277
1278 with evaluation_policy("minimal"):
1278 with evaluation_policy("minimal"):
1279 with pytest.raises(AssertionError):
1279 with pytest.raises(AssertionError):
1280 assert_completion(line_buffer="nested['x'][")
1280 assert_completion(line_buffer="nested['x'][")
1281
1281
1282 # greedy flag
1282 # greedy flag
1283 def assert_completion(**kwargs):
1283 def assert_completion(**kwargs):
1284 _, matches = complete(**kwargs)
1284 _, matches = complete(**kwargs)
1285 self.assertIn("get()['abc']", matches)
1285 self.assertIn("get()['abc']", matches)
1286
1286
1287 assert_no_completion(line_buffer="get()[")
1287 assert_no_completion(line_buffer="get()[")
1288 with greedy_completion():
1288 with greedy_completion():
1289 assert_completion(line_buffer="get()[")
1289 assert_completion(line_buffer="get()[")
1290 assert_completion(line_buffer="get()['")
1290 assert_completion(line_buffer="get()['")
1291 assert_completion(line_buffer="get()['a")
1291 assert_completion(line_buffer="get()['a")
1292 assert_completion(line_buffer="get()['ab")
1292 assert_completion(line_buffer="get()['ab")
1293 assert_completion(line_buffer="get()['abc")
1293 assert_completion(line_buffer="get()['abc")
1294
1294
1295 def test_dict_key_completion_bytes(self):
1295 def test_dict_key_completion_bytes(self):
1296 """Test handling of bytes in dict key completion"""
1296 """Test handling of bytes in dict key completion"""
1297 ip = get_ipython()
1297 ip = get_ipython()
1298 complete = ip.Completer.complete
1298 complete = ip.Completer.complete
1299
1299
1300 ip.user_ns["d"] = {"abc": None, b"abd": None}
1300 ip.user_ns["d"] = {"abc": None, b"abd": None}
1301
1301
1302 _, matches = complete(line_buffer="d[")
1302 _, matches = complete(line_buffer="d[")
1303 self.assertIn("'abc'", matches)
1303 self.assertIn("'abc'", matches)
1304 self.assertIn("b'abd'", matches)
1304 self.assertIn("b'abd'", matches)
1305
1305
1306 if False: # not currently implemented
1306 if False: # not currently implemented
1307 _, matches = complete(line_buffer="d[b")
1307 _, matches = complete(line_buffer="d[b")
1308 self.assertIn("b'abd'", matches)
1308 self.assertIn("b'abd'", matches)
1309 self.assertNotIn("b'abc'", matches)
1309 self.assertNotIn("b'abc'", matches)
1310
1310
1311 _, matches = complete(line_buffer="d[b'")
1311 _, matches = complete(line_buffer="d[b'")
1312 self.assertIn("abd", matches)
1312 self.assertIn("abd", matches)
1313 self.assertNotIn("abc", matches)
1313 self.assertNotIn("abc", matches)
1314
1314
1315 _, matches = complete(line_buffer="d[B'")
1315 _, matches = complete(line_buffer="d[B'")
1316 self.assertIn("abd", matches)
1316 self.assertIn("abd", matches)
1317 self.assertNotIn("abc", matches)
1317 self.assertNotIn("abc", matches)
1318
1318
1319 _, matches = complete(line_buffer="d['")
1319 _, matches = complete(line_buffer="d['")
1320 self.assertIn("abc", matches)
1320 self.assertIn("abc", matches)
1321 self.assertNotIn("abd", matches)
1321 self.assertNotIn("abd", matches)
1322
1322
1323 def test_dict_key_completion_unicode_py3(self):
1323 def test_dict_key_completion_unicode_py3(self):
1324 """Test handling of unicode in dict key completion"""
1324 """Test handling of unicode in dict key completion"""
1325 ip = get_ipython()
1325 ip = get_ipython()
1326 complete = ip.Completer.complete
1326 complete = ip.Completer.complete
1327
1327
1328 ip.user_ns["d"] = {"a\u05d0": None}
1328 ip.user_ns["d"] = {"a\u05d0": None}
1329
1329
1330 # query using escape
1330 # query using escape
1331 if sys.platform != "win32":
1331 if sys.platform != "win32":
1332 # Known failure on Windows
1332 # Known failure on Windows
1333 _, matches = complete(line_buffer="d['a\\u05d0")
1333 _, matches = complete(line_buffer="d['a\\u05d0")
1334 self.assertIn("u05d0", matches) # tokenized after \\
1334 self.assertIn("u05d0", matches) # tokenized after \\
1335
1335
1336 # query using character
1336 # query using character
1337 _, matches = complete(line_buffer="d['a\u05d0")
1337 _, matches = complete(line_buffer="d['a\u05d0")
1338 self.assertIn("a\u05d0", matches)
1338 self.assertIn("a\u05d0", matches)
1339
1339
1340 with greedy_completion():
1340 with greedy_completion():
1341 # query using escape
1341 # query using escape
1342 _, matches = complete(line_buffer="d['a\\u05d0")
1342 _, matches = complete(line_buffer="d['a\\u05d0")
1343 self.assertIn("d['a\\u05d0']", matches) # tokenized after \\
1343 self.assertIn("d['a\\u05d0']", matches) # tokenized after \\
1344
1344
1345 # query using character
1345 # query using character
1346 _, matches = complete(line_buffer="d['a\u05d0")
1346 _, matches = complete(line_buffer="d['a\u05d0")
1347 self.assertIn("d['a\u05d0']", matches)
1347 self.assertIn("d['a\u05d0']", matches)
1348
1348
1349 @dec.skip_without("numpy")
1349 @dec.skip_without("numpy")
1350 def test_struct_array_key_completion(self):
1350 def test_struct_array_key_completion(self):
1351 """Test dict key completion applies to numpy struct arrays"""
1351 """Test dict key completion applies to numpy struct arrays"""
1352 import numpy
1352 import numpy
1353
1353
1354 ip = get_ipython()
1354 ip = get_ipython()
1355 complete = ip.Completer.complete
1355 complete = ip.Completer.complete
1356 ip.user_ns["d"] = numpy.array([], dtype=[("hello", "f"), ("world", "f")])
1356 ip.user_ns["d"] = numpy.array([], dtype=[("hello", "f"), ("world", "f")])
1357 _, matches = complete(line_buffer="d['")
1357 _, matches = complete(line_buffer="d['")
1358 self.assertIn("hello", matches)
1358 self.assertIn("hello", matches)
1359 self.assertIn("world", matches)
1359 self.assertIn("world", matches)
1360 # complete on the numpy struct itself
1360 # complete on the numpy struct itself
1361 dt = numpy.dtype(
1361 dt = numpy.dtype(
1362 [("my_head", [("my_dt", ">u4"), ("my_df", ">u4")]), ("my_data", ">f4", 5)]
1362 [("my_head", [("my_dt", ">u4"), ("my_df", ">u4")]), ("my_data", ">f4", 5)]
1363 )
1363 )
1364 x = numpy.zeros(2, dtype=dt)
1364 x = numpy.zeros(2, dtype=dt)
1365 ip.user_ns["d"] = x[1]
1365 ip.user_ns["d"] = x[1]
1366 _, matches = complete(line_buffer="d['")
1366 _, matches = complete(line_buffer="d['")
1367 self.assertIn("my_head", matches)
1367 self.assertIn("my_head", matches)
1368 self.assertIn("my_data", matches)
1368 self.assertIn("my_data", matches)
1369
1369
1370 def completes_on_nested():
1370 def completes_on_nested():
1371 ip.user_ns["d"] = numpy.zeros(2, dtype=dt)
1371 ip.user_ns["d"] = numpy.zeros(2, dtype=dt)
1372 _, matches = complete(line_buffer="d[1]['my_head']['")
1372 _, matches = complete(line_buffer="d[1]['my_head']['")
1373 self.assertTrue(any(["my_dt" in m for m in matches]))
1373 self.assertTrue(any(["my_dt" in m for m in matches]))
1374 self.assertTrue(any(["my_df" in m for m in matches]))
1374 self.assertTrue(any(["my_df" in m for m in matches]))
1375 # complete on a nested level
1375 # complete on a nested level
1376 with greedy_completion():
1376 with greedy_completion():
1377 completes_on_nested()
1377 completes_on_nested()
1378
1378
1379 with evaluation_policy("limited"):
1379 with evaluation_policy("limited"):
1380 completes_on_nested()
1380 completes_on_nested()
1381
1381
1382 with evaluation_policy("minimal"):
1382 with evaluation_policy("minimal"):
1383 with pytest.raises(AssertionError):
1383 with pytest.raises(AssertionError):
1384 completes_on_nested()
1384 completes_on_nested()
1385
1385
1386 @dec.skip_without("pandas")
1386 @dec.skip_without("pandas")
1387 def test_dataframe_key_completion(self):
1387 def test_dataframe_key_completion(self):
1388 """Test dict key completion applies to pandas DataFrames"""
1388 """Test dict key completion applies to pandas DataFrames"""
1389 import pandas
1389 import pandas
1390
1390
1391 ip = get_ipython()
1391 ip = get_ipython()
1392 complete = ip.Completer.complete
1392 complete = ip.Completer.complete
1393 ip.user_ns["d"] = pandas.DataFrame({"hello": [1], "world": [2]})
1393 ip.user_ns["d"] = pandas.DataFrame({"hello": [1], "world": [2]})
1394 _, matches = complete(line_buffer="d['")
1394 _, matches = complete(line_buffer="d['")
1395 self.assertIn("hello", matches)
1395 self.assertIn("hello", matches)
1396 self.assertIn("world", matches)
1396 self.assertIn("world", matches)
1397 _, matches = complete(line_buffer="d.loc[:, '")
1397 _, matches = complete(line_buffer="d.loc[:, '")
1398 self.assertIn("hello", matches)
1398 self.assertIn("hello", matches)
1399 self.assertIn("world", matches)
1399 self.assertIn("world", matches)
1400 _, matches = complete(line_buffer="d.loc[1:, '")
1400 _, matches = complete(line_buffer="d.loc[1:, '")
1401 self.assertIn("hello", matches)
1401 self.assertIn("hello", matches)
1402 _, matches = complete(line_buffer="d.loc[1:1, '")
1402 _, matches = complete(line_buffer="d.loc[1:1, '")
1403 self.assertIn("hello", matches)
1403 self.assertIn("hello", matches)
1404 _, matches = complete(line_buffer="d.loc[1:1:-1, '")
1404 _, matches = complete(line_buffer="d.loc[1:1:-1, '")
1405 self.assertIn("hello", matches)
1405 self.assertIn("hello", matches)
1406 _, matches = complete(line_buffer="d.loc[::, '")
1406 _, matches = complete(line_buffer="d.loc[::, '")
1407 self.assertIn("hello", matches)
1407 self.assertIn("hello", matches)
1408
1408
1409 def test_dict_key_completion_invalids(self):
1409 def test_dict_key_completion_invalids(self):
1410 """Smoke test cases dict key completion can't handle"""
1410 """Smoke test cases dict key completion can't handle"""
1411 ip = get_ipython()
1411 ip = get_ipython()
1412 complete = ip.Completer.complete
1412 complete = ip.Completer.complete
1413
1413
1414 ip.user_ns["no_getitem"] = None
1414 ip.user_ns["no_getitem"] = None
1415 ip.user_ns["no_keys"] = []
1415 ip.user_ns["no_keys"] = []
1416 ip.user_ns["cant_call_keys"] = dict
1416 ip.user_ns["cant_call_keys"] = dict
1417 ip.user_ns["empty"] = {}
1417 ip.user_ns["empty"] = {}
1418 ip.user_ns["d"] = {"abc": 5}
1418 ip.user_ns["d"] = {"abc": 5}
1419
1419
1420 _, matches = complete(line_buffer="no_getitem['")
1420 _, matches = complete(line_buffer="no_getitem['")
1421 _, matches = complete(line_buffer="no_keys['")
1421 _, matches = complete(line_buffer="no_keys['")
1422 _, matches = complete(line_buffer="cant_call_keys['")
1422 _, matches = complete(line_buffer="cant_call_keys['")
1423 _, matches = complete(line_buffer="empty['")
1423 _, matches = complete(line_buffer="empty['")
1424 _, matches = complete(line_buffer="name_error['")
1424 _, matches = complete(line_buffer="name_error['")
1425 _, matches = complete(line_buffer="d['\\") # incomplete escape
1425 _, matches = complete(line_buffer="d['\\") # incomplete escape
1426
1426
1427 def test_object_key_completion(self):
1427 def test_object_key_completion(self):
1428 ip = get_ipython()
1428 ip = get_ipython()
1429 ip.user_ns["key_completable"] = KeyCompletable(["qwerty", "qwick"])
1429 ip.user_ns["key_completable"] = KeyCompletable(["qwerty", "qwick"])
1430
1430
1431 _, matches = ip.Completer.complete(line_buffer="key_completable['qw")
1431 _, matches = ip.Completer.complete(line_buffer="key_completable['qw")
1432 self.assertIn("qwerty", matches)
1432 self.assertIn("qwerty", matches)
1433 self.assertIn("qwick", matches)
1433 self.assertIn("qwick", matches)
1434
1434
1435 def test_class_key_completion(self):
1435 def test_class_key_completion(self):
1436 ip = get_ipython()
1436 ip = get_ipython()
1437 NamedInstanceClass("qwerty")
1437 NamedInstanceClass("qwerty")
1438 NamedInstanceClass("qwick")
1438 NamedInstanceClass("qwick")
1439 ip.user_ns["named_instance_class"] = NamedInstanceClass
1439 ip.user_ns["named_instance_class"] = NamedInstanceClass
1440
1440
1441 _, matches = ip.Completer.complete(line_buffer="named_instance_class['qw")
1441 _, matches = ip.Completer.complete(line_buffer="named_instance_class['qw")
1442 self.assertIn("qwerty", matches)
1442 self.assertIn("qwerty", matches)
1443 self.assertIn("qwick", matches)
1443 self.assertIn("qwick", matches)
1444
1444
1445 def test_tryimport(self):
1445 def test_tryimport(self):
1446 """
1446 """
1447 Test that try-import don't crash on trailing dot, and import modules before
1447 Test that try-import don't crash on trailing dot, and import modules before
1448 """
1448 """
1449 from IPython.core.completerlib import try_import
1449 from IPython.core.completerlib import try_import
1450
1450
1451 assert try_import("IPython.")
1451 assert try_import("IPython.")
1452
1452
1453 def test_aimport_module_completer(self):
1453 def test_aimport_module_completer(self):
1454 ip = get_ipython()
1454 ip = get_ipython()
1455 _, matches = ip.complete("i", "%aimport i")
1455 _, matches = ip.complete("i", "%aimport i")
1456 self.assertIn("io", matches)
1456 self.assertIn("io", matches)
1457 self.assertNotIn("int", matches)
1457 self.assertNotIn("int", matches)
1458
1458
1459 def test_nested_import_module_completer(self):
1459 def test_nested_import_module_completer(self):
1460 ip = get_ipython()
1460 ip = get_ipython()
1461 _, matches = ip.complete(None, "import IPython.co", 17)
1461 _, matches = ip.complete(None, "import IPython.co", 17)
1462 self.assertIn("IPython.core", matches)
1462 self.assertIn("IPython.core", matches)
1463 self.assertNotIn("import IPython.core", matches)
1463 self.assertNotIn("import IPython.core", matches)
1464 self.assertNotIn("IPython.display", matches)
1464 self.assertNotIn("IPython.display", matches)
1465
1465
1466 def test_import_module_completer(self):
1466 def test_import_module_completer(self):
1467 ip = get_ipython()
1467 ip = get_ipython()
1468 _, matches = ip.complete("i", "import i")
1468 _, matches = ip.complete("i", "import i")
1469 self.assertIn("io", matches)
1469 self.assertIn("io", matches)
1470 self.assertNotIn("int", matches)
1470 self.assertNotIn("int", matches)
1471
1471
1472 def test_from_module_completer(self):
1472 def test_from_module_completer(self):
1473 ip = get_ipython()
1473 ip = get_ipython()
1474 _, matches = ip.complete("B", "from io import B", 16)
1474 _, matches = ip.complete("B", "from io import B", 16)
1475 self.assertIn("BytesIO", matches)
1475 self.assertIn("BytesIO", matches)
1476 self.assertNotIn("BaseException", matches)
1476 self.assertNotIn("BaseException", matches)
1477
1477
1478 def test_snake_case_completion(self):
1478 def test_snake_case_completion(self):
1479 ip = get_ipython()
1479 ip = get_ipython()
1480 ip.Completer.use_jedi = False
1480 ip.Completer.use_jedi = False
1481 ip.user_ns["some_three"] = 3
1481 ip.user_ns["some_three"] = 3
1482 ip.user_ns["some_four"] = 4
1482 ip.user_ns["some_four"] = 4
1483 _, matches = ip.complete("s_", "print(s_f")
1483 _, matches = ip.complete("s_", "print(s_f")
1484 self.assertIn("some_three", matches)
1484 self.assertIn("some_three", matches)
1485 self.assertIn("some_four", matches)
1485 self.assertIn("some_four", matches)
1486
1486
1487 def test_mix_terms(self):
1487 def test_mix_terms(self):
1488 ip = get_ipython()
1488 ip = get_ipython()
1489 from textwrap import dedent
1489 from textwrap import dedent
1490
1490
1491 ip.Completer.use_jedi = False
1491 ip.Completer.use_jedi = False
1492 ip.ex(
1492 ip.ex(
1493 dedent(
1493 dedent(
1494 """
1494 """
1495 class Test:
1495 class Test:
1496 def meth(self, meth_arg1):
1496 def meth(self, meth_arg1):
1497 print("meth")
1497 print("meth")
1498
1498
1499 def meth_1(self, meth1_arg1, meth1_arg2):
1499 def meth_1(self, meth1_arg1, meth1_arg2):
1500 print("meth1")
1500 print("meth1")
1501
1501
1502 def meth_2(self, meth2_arg1, meth2_arg2):
1502 def meth_2(self, meth2_arg1, meth2_arg2):
1503 print("meth2")
1503 print("meth2")
1504 test = Test()
1504 test = Test()
1505 """
1505 """
1506 )
1506 )
1507 )
1507 )
1508 _, matches = ip.complete(None, "test.meth(")
1508 _, matches = ip.complete(None, "test.meth(")
1509 self.assertIn("meth_arg1=", matches)
1509 self.assertIn("meth_arg1=", matches)
1510 self.assertNotIn("meth2_arg1=", matches)
1510 self.assertNotIn("meth2_arg1=", matches)
1511
1511
1512 def test_percent_symbol_restrict_to_magic_completions(self):
1512 def test_percent_symbol_restrict_to_magic_completions(self):
1513 ip = get_ipython()
1513 ip = get_ipython()
1514 completer = ip.Completer
1514 completer = ip.Completer
1515 text = "%a"
1515 text = "%a"
1516
1516
1517 with provisionalcompleter():
1517 with provisionalcompleter():
1518 completer.use_jedi = True
1518 completer.use_jedi = True
1519 completions = completer.completions(text, len(text))
1519 completions = completer.completions(text, len(text))
1520 for c in completions:
1520 for c in completions:
1521 self.assertEqual(c.text[0], "%")
1521 self.assertEqual(c.text[0], "%")
1522
1522
1523 def test_fwd_unicode_restricts(self):
1523 def test_fwd_unicode_restricts(self):
1524 ip = get_ipython()
1524 ip = get_ipython()
1525 completer = ip.Completer
1525 completer = ip.Completer
1526 text = "\\ROMAN NUMERAL FIVE"
1526 text = "\\ROMAN NUMERAL FIVE"
1527
1527
1528 with provisionalcompleter():
1528 with provisionalcompleter():
1529 completer.use_jedi = True
1529 completer.use_jedi = True
1530 completions = [
1530 completions = [
1531 completion.text for completion in completer.completions(text, len(text))
1531 completion.text for completion in completer.completions(text, len(text))
1532 ]
1532 ]
1533 self.assertEqual(completions, ["\u2164"])
1533 self.assertEqual(completions, ["\u2164"])
1534
1534
1535 def test_dict_key_restrict_to_dicts(self):
1535 def test_dict_key_restrict_to_dicts(self):
1536 """Test that dict key suppresses non-dict completion items"""
1536 """Test that dict key suppresses non-dict completion items"""
1537 ip = get_ipython()
1537 ip = get_ipython()
1538 c = ip.Completer
1538 c = ip.Completer
1539 d = {"abc": None}
1539 d = {"abc": None}
1540 ip.user_ns["d"] = d
1540 ip.user_ns["d"] = d
1541
1541
1542 text = 'd["a'
1542 text = 'd["a'
1543
1543
1544 def _():
1544 def _():
1545 with provisionalcompleter():
1545 with provisionalcompleter():
1546 c.use_jedi = True
1546 c.use_jedi = True
1547 return [
1547 return [
1548 completion.text for completion in c.completions(text, len(text))
1548 completion.text for completion in c.completions(text, len(text))
1549 ]
1549 ]
1550
1550
1551 completions = _()
1551 completions = _()
1552 self.assertEqual(completions, ["abc"])
1552 self.assertEqual(completions, ["abc"])
1553
1553
1554 # check that it can be disabled in granular manner:
1554 # check that it can be disabled in granular manner:
1555 cfg = Config()
1555 cfg = Config()
1556 cfg.IPCompleter.suppress_competing_matchers = {
1556 cfg.IPCompleter.suppress_competing_matchers = {
1557 "IPCompleter.dict_key_matcher": False
1557 "IPCompleter.dict_key_matcher": False
1558 }
1558 }
1559 c.update_config(cfg)
1559 c.update_config(cfg)
1560
1560
1561 completions = _()
1561 completions = _()
1562 self.assertIn("abc", completions)
1562 self.assertIn("abc", completions)
1563 self.assertGreater(len(completions), 1)
1563 self.assertGreater(len(completions), 1)
1564
1564
1565 def test_matcher_suppression(self):
1565 def test_matcher_suppression(self):
1566 @completion_matcher(identifier="a_matcher")
1566 @completion_matcher(identifier="a_matcher")
1567 def a_matcher(text):
1567 def a_matcher(text):
1568 return ["completion_a"]
1568 return ["completion_a"]
1569
1569
1570 @completion_matcher(identifier="b_matcher", api_version=2)
1570 @completion_matcher(identifier="b_matcher", api_version=2)
1571 def b_matcher(context: CompletionContext):
1571 def b_matcher(context: CompletionContext):
1572 text = context.token
1572 text = context.token
1573 result = {"completions": [SimpleCompletion("completion_b")]}
1573 result = {"completions": [SimpleCompletion("completion_b")]}
1574
1574
1575 if text == "suppress c":
1575 if text == "suppress c":
1576 result["suppress"] = {"c_matcher"}
1576 result["suppress"] = {"c_matcher"}
1577
1577
1578 if text.startswith("suppress all"):
1578 if text.startswith("suppress all"):
1579 result["suppress"] = True
1579 result["suppress"] = True
1580 if text == "suppress all but c":
1580 if text == "suppress all but c":
1581 result["do_not_suppress"] = {"c_matcher"}
1581 result["do_not_suppress"] = {"c_matcher"}
1582 if text == "suppress all but a":
1582 if text == "suppress all but a":
1583 result["do_not_suppress"] = {"a_matcher"}
1583 result["do_not_suppress"] = {"a_matcher"}
1584
1584
1585 return result
1585 return result
1586
1586
1587 @completion_matcher(identifier="c_matcher")
1587 @completion_matcher(identifier="c_matcher")
1588 def c_matcher(text):
1588 def c_matcher(text):
1589 return ["completion_c"]
1589 return ["completion_c"]
1590
1590
1591 with custom_matchers([a_matcher, b_matcher, c_matcher]):
1591 with custom_matchers([a_matcher, b_matcher, c_matcher]):
1592 ip = get_ipython()
1592 ip = get_ipython()
1593 c = ip.Completer
1593 c = ip.Completer
1594
1594
1595 def _(text, expected):
1595 def _(text, expected):
1596 c.use_jedi = False
1596 c.use_jedi = False
1597 s, matches = c.complete(text)
1597 s, matches = c.complete(text)
1598 self.assertEqual(expected, matches)
1598 self.assertEqual(expected, matches)
1599
1599
1600 _("do not suppress", ["completion_a", "completion_b", "completion_c"])
1600 _("do not suppress", ["completion_a", "completion_b", "completion_c"])
1601 _("suppress all", ["completion_b"])
1601 _("suppress all", ["completion_b"])
1602 _("suppress all but a", ["completion_a", "completion_b"])
1602 _("suppress all but a", ["completion_a", "completion_b"])
1603 _("suppress all but c", ["completion_b", "completion_c"])
1603 _("suppress all but c", ["completion_b", "completion_c"])
1604
1604
1605 def configure(suppression_config):
1605 def configure(suppression_config):
1606 cfg = Config()
1606 cfg = Config()
1607 cfg.IPCompleter.suppress_competing_matchers = suppression_config
1607 cfg.IPCompleter.suppress_competing_matchers = suppression_config
1608 c.update_config(cfg)
1608 c.update_config(cfg)
1609
1609
1610 # test that configuration takes priority over the run-time decisions
1610 # test that configuration takes priority over the run-time decisions
1611
1611
1612 configure(False)
1612 configure(False)
1613 _("suppress all", ["completion_a", "completion_b", "completion_c"])
1613 _("suppress all", ["completion_a", "completion_b", "completion_c"])
1614
1614
1615 configure({"b_matcher": False})
1615 configure({"b_matcher": False})
1616 _("suppress all", ["completion_a", "completion_b", "completion_c"])
1616 _("suppress all", ["completion_a", "completion_b", "completion_c"])
1617
1617
1618 configure({"a_matcher": False})
1618 configure({"a_matcher": False})
1619 _("suppress all", ["completion_b"])
1619 _("suppress all", ["completion_b"])
1620
1620
1621 configure({"b_matcher": True})
1621 configure({"b_matcher": True})
1622 _("do not suppress", ["completion_b"])
1622 _("do not suppress", ["completion_b"])
1623
1623
1624 configure(True)
1624 configure(True)
1625 _("do not suppress", ["completion_a"])
1625 _("do not suppress", ["completion_a"])
1626
1626
1627 def test_matcher_suppression_with_iterator(self):
1627 def test_matcher_suppression_with_iterator(self):
1628 @completion_matcher(identifier="matcher_returning_iterator")
1628 @completion_matcher(identifier="matcher_returning_iterator")
1629 def matcher_returning_iterator(text):
1629 def matcher_returning_iterator(text):
1630 return iter(["completion_iter"])
1630 return iter(["completion_iter"])
1631
1631
1632 @completion_matcher(identifier="matcher_returning_list")
1632 @completion_matcher(identifier="matcher_returning_list")
1633 def matcher_returning_list(text):
1633 def matcher_returning_list(text):
1634 return ["completion_list"]
1634 return ["completion_list"]
1635
1635
1636 with custom_matchers([matcher_returning_iterator, matcher_returning_list]):
1636 with custom_matchers([matcher_returning_iterator, matcher_returning_list]):
1637 ip = get_ipython()
1637 ip = get_ipython()
1638 c = ip.Completer
1638 c = ip.Completer
1639
1639
1640 def _(text, expected):
1640 def _(text, expected):
1641 c.use_jedi = False
1641 c.use_jedi = False
1642 s, matches = c.complete(text)
1642 s, matches = c.complete(text)
1643 self.assertEqual(expected, matches)
1643 self.assertEqual(expected, matches)
1644
1644
1645 def configure(suppression_config):
1645 def configure(suppression_config):
1646 cfg = Config()
1646 cfg = Config()
1647 cfg.IPCompleter.suppress_competing_matchers = suppression_config
1647 cfg.IPCompleter.suppress_competing_matchers = suppression_config
1648 c.update_config(cfg)
1648 c.update_config(cfg)
1649
1649
1650 configure(False)
1650 configure(False)
1651 _("---", ["completion_iter", "completion_list"])
1651 _("---", ["completion_iter", "completion_list"])
1652
1652
1653 configure(True)
1653 configure(True)
1654 _("---", ["completion_iter"])
1654 _("---", ["completion_iter"])
1655
1655
1656 configure(None)
1656 configure(None)
1657 _("--", ["completion_iter", "completion_list"])
1657 _("--", ["completion_iter", "completion_list"])
1658
1658
1659 @pytest.mark.xfail(
1659 @pytest.mark.xfail(
1660 sys.version_info.releaselevel in ("alpha",),
1660 sys.version_info.releaselevel in ("alpha",),
1661 reason="Parso does not yet parse 3.13",
1661 reason="Parso does not yet parse 3.13",
1662 )
1662 )
1663 def test_matcher_suppression_with_jedi(self):
1663 def test_matcher_suppression_with_jedi(self):
1664 ip = get_ipython()
1664 ip = get_ipython()
1665 c = ip.Completer
1665 c = ip.Completer
1666 c.use_jedi = True
1666 c.use_jedi = True
1667
1667
1668 def configure(suppression_config):
1668 def configure(suppression_config):
1669 cfg = Config()
1669 cfg = Config()
1670 cfg.IPCompleter.suppress_competing_matchers = suppression_config
1670 cfg.IPCompleter.suppress_competing_matchers = suppression_config
1671 c.update_config(cfg)
1671 c.update_config(cfg)
1672
1672
1673 def _():
1673 def _():
1674 with provisionalcompleter():
1674 with provisionalcompleter():
1675 matches = [completion.text for completion in c.completions("dict.", 5)]
1675 matches = [completion.text for completion in c.completions("dict.", 5)]
1676 self.assertIn("keys", matches)
1676 self.assertIn("keys", matches)
1677
1677
1678 configure(False)
1678 configure(False)
1679 _()
1679 _()
1680
1680
1681 configure(True)
1681 configure(True)
1682 _()
1682 _()
1683
1683
1684 configure(None)
1684 configure(None)
1685 _()
1685 _()
1686
1686
1687 def test_matcher_disabling(self):
1687 def test_matcher_disabling(self):
1688 @completion_matcher(identifier="a_matcher")
1688 @completion_matcher(identifier="a_matcher")
1689 def a_matcher(text):
1689 def a_matcher(text):
1690 return ["completion_a"]
1690 return ["completion_a"]
1691
1691
1692 @completion_matcher(identifier="b_matcher")
1692 @completion_matcher(identifier="b_matcher")
1693 def b_matcher(text):
1693 def b_matcher(text):
1694 return ["completion_b"]
1694 return ["completion_b"]
1695
1695
1696 def _(expected):
1696 def _(expected):
1697 s, matches = c.complete("completion_")
1697 s, matches = c.complete("completion_")
1698 self.assertEqual(expected, matches)
1698 self.assertEqual(expected, matches)
1699
1699
1700 with custom_matchers([a_matcher, b_matcher]):
1700 with custom_matchers([a_matcher, b_matcher]):
1701 ip = get_ipython()
1701 ip = get_ipython()
1702 c = ip.Completer
1702 c = ip.Completer
1703
1703
1704 _(["completion_a", "completion_b"])
1704 _(["completion_a", "completion_b"])
1705
1705
1706 cfg = Config()
1706 cfg = Config()
1707 cfg.IPCompleter.disable_matchers = ["b_matcher"]
1707 cfg.IPCompleter.disable_matchers = ["b_matcher"]
1708 c.update_config(cfg)
1708 c.update_config(cfg)
1709
1709
1710 _(["completion_a"])
1710 _(["completion_a"])
1711
1711
1712 cfg.IPCompleter.disable_matchers = []
1712 cfg.IPCompleter.disable_matchers = []
1713 c.update_config(cfg)
1713 c.update_config(cfg)
1714
1714
1715 def test_matcher_priority(self):
1715 def test_matcher_priority(self):
1716 @completion_matcher(identifier="a_matcher", priority=0, api_version=2)
1716 @completion_matcher(identifier="a_matcher", priority=0, api_version=2)
1717 def a_matcher(text):
1717 def a_matcher(text):
1718 return {"completions": [SimpleCompletion("completion_a")], "suppress": True}
1718 return {"completions": [SimpleCompletion("completion_a")], "suppress": True}
1719
1719
1720 @completion_matcher(identifier="b_matcher", priority=2, api_version=2)
1720 @completion_matcher(identifier="b_matcher", priority=2, api_version=2)
1721 def b_matcher(text):
1721 def b_matcher(text):
1722 return {"completions": [SimpleCompletion("completion_b")], "suppress": True}
1722 return {"completions": [SimpleCompletion("completion_b")], "suppress": True}
1723
1723
1724 def _(expected):
1724 def _(expected):
1725 s, matches = c.complete("completion_")
1725 s, matches = c.complete("completion_")
1726 self.assertEqual(expected, matches)
1726 self.assertEqual(expected, matches)
1727
1727
1728 with custom_matchers([a_matcher, b_matcher]):
1728 with custom_matchers([a_matcher, b_matcher]):
1729 ip = get_ipython()
1729 ip = get_ipython()
1730 c = ip.Completer
1730 c = ip.Completer
1731
1731
1732 _(["completion_b"])
1732 _(["completion_b"])
1733 a_matcher.matcher_priority = 3
1733 a_matcher.matcher_priority = 3
1734 _(["completion_a"])
1734 _(["completion_a"])
1735
1735
1736
1736
1737 @pytest.mark.parametrize(
1737 @pytest.mark.parametrize(
1738 "input, expected",
1738 "input, expected",
1739 [
1739 [
1740 ["1.234", "1.234"],
1740 ["1.234", "1.234"],
1741 # should match signed numbers
1741 # should match signed numbers
1742 ["+1", "+1"],
1742 ["+1", "+1"],
1743 ["-1", "-1"],
1743 ["-1", "-1"],
1744 ["-1.0", "-1.0"],
1744 ["-1.0", "-1.0"],
1745 ["-1.", "-1."],
1745 ["-1.", "-1."],
1746 ["+1.", "+1."],
1746 ["+1.", "+1."],
1747 [".1", ".1"],
1747 [".1", ".1"],
1748 # should not match non-numbers
1748 # should not match non-numbers
1749 ["1..", None],
1749 ["1..", None],
1750 ["..", None],
1750 ["..", None],
1751 [".1.", None],
1751 [".1.", None],
1752 # should match after comma
1752 # should match after comma
1753 [",1", "1"],
1753 [",1", "1"],
1754 [", 1", "1"],
1754 [", 1", "1"],
1755 [", .1", ".1"],
1755 [", .1", ".1"],
1756 [", +.1", "+.1"],
1756 [", +.1", "+.1"],
1757 # should not match after trailing spaces
1757 # should not match after trailing spaces
1758 [".1 ", None],
1758 [".1 ", None],
1759 # some complex cases
1759 # some complex cases
1760 ["0b_0011_1111_0100_1110", "0b_0011_1111_0100_1110"],
1760 ["0b_0011_1111_0100_1110", "0b_0011_1111_0100_1110"],
1761 ["0xdeadbeef", "0xdeadbeef"],
1761 ["0xdeadbeef", "0xdeadbeef"],
1762 ["0b_1110_0101", "0b_1110_0101"],
1762 ["0b_1110_0101", "0b_1110_0101"],
1763 # should not match if in an operation
1763 # should not match if in an operation
1764 ["1 + 1", None],
1764 ["1 + 1", None],
1765 [", 1 + 1", None],
1765 [", 1 + 1", None],
1766 ],
1766 ],
1767 )
1767 )
1768 def test_match_numeric_literal_for_dict_key(input, expected):
1768 def test_match_numeric_literal_for_dict_key(input, expected):
1769 assert _match_number_in_dict_key_prefix(input) == expected
1769 assert _match_number_in_dict_key_prefix(input) == expected
@@ -1,513 +1,513
1 # Copyright (c) IPython Development Team.
1 # Copyright (c) IPython Development Team.
2 # Distributed under the terms of the Modified BSD License.
2 # Distributed under the terms of the Modified BSD License.
3
3
4 import json
4 import json
5 import os
5 import os
6 import warnings
6 import warnings
7
7
8 from unittest import mock
8 from unittest import mock
9
9
10 import pytest
10 import pytest
11
11
12 from IPython import display
12 from IPython import display
13 from IPython.core.getipython import get_ipython
13 from IPython.core.getipython import get_ipython
14 from IPython.utils.io import capture_output
14 from IPython.utils.io import capture_output
15 from IPython.utils.tempdir import NamedFileInTemporaryDirectory
15 from IPython.utils.tempdir import NamedFileInTemporaryDirectory
16 from IPython import paths as ipath
16 from IPython import paths as ipath
17 from IPython.testing.tools import AssertNotPrints
17 from IPython.testing.tools import AssertNotPrints
18
18
19 import IPython.testing.decorators as dec
19 import IPython.testing.decorators as dec
20
20
21 def test_image_size():
21 def test_image_size():
22 """Simple test for display.Image(args, width=x,height=y)"""
22 """Simple test for display.Image(args, width=x,height=y)"""
23 thisurl = 'http://www.google.fr/images/srpr/logo3w.png'
23 thisurl = 'http://www.google.fr/images/srpr/logo3w.png'
24 img = display.Image(url=thisurl, width=200, height=200)
24 img = display.Image(url=thisurl, width=200, height=200)
25 assert '<img src="%s" width="200" height="200"/>' % (thisurl) == img._repr_html_()
25 assert '<img src="%s" width="200" height="200"/>' % (thisurl) == img._repr_html_()
26 img = display.Image(url=thisurl, metadata={'width':200, 'height':200})
26 img = display.Image(url=thisurl, metadata={'width':200, 'height':200})
27 assert '<img src="%s" width="200" height="200"/>' % (thisurl) == img._repr_html_()
27 assert '<img src="%s" width="200" height="200"/>' % (thisurl) == img._repr_html_()
28 img = display.Image(url=thisurl, width=200)
28 img = display.Image(url=thisurl, width=200)
29 assert '<img src="%s" width="200"/>' % (thisurl) == img._repr_html_()
29 assert '<img src="%s" width="200"/>' % (thisurl) == img._repr_html_()
30 img = display.Image(url=thisurl)
30 img = display.Image(url=thisurl)
31 assert '<img src="%s"/>' % (thisurl) == img._repr_html_()
31 assert '<img src="%s"/>' % (thisurl) == img._repr_html_()
32 img = display.Image(url=thisurl, unconfined=True)
32 img = display.Image(url=thisurl, unconfined=True)
33 assert '<img src="%s" class="unconfined"/>' % (thisurl) == img._repr_html_()
33 assert '<img src="%s" class="unconfined"/>' % (thisurl) == img._repr_html_()
34
34
35
35
36 def test_image_mimes():
36 def test_image_mimes():
37 fmt = get_ipython().display_formatter.format
37 fmt = get_ipython().display_formatter.format
38 for format in display.Image._ACCEPTABLE_EMBEDDINGS:
38 for format in display.Image._ACCEPTABLE_EMBEDDINGS:
39 mime = display.Image._MIMETYPES[format]
39 mime = display.Image._MIMETYPES[format]
40 img = display.Image(b'garbage', format=format)
40 img = display.Image(b'garbage', format=format)
41 data, metadata = fmt(img)
41 data, metadata = fmt(img)
42 assert sorted(data) == sorted([mime, "text/plain"])
42 assert sorted(data) == sorted([mime, "text/plain"])
43
43
44
44
45 def test_geojson():
45 def test_geojson():
46
46
47 gj = display.GeoJSON(data={
47 gj = display.GeoJSON(data={
48 "type": "Feature",
48 "type": "Feature",
49 "geometry": {
49 "geometry": {
50 "type": "Point",
50 "type": "Point",
51 "coordinates": [-81.327, 296.038]
51 "coordinates": [-81.327, 296.038]
52 },
52 },
53 "properties": {
53 "properties": {
54 "name": "Inca City"
54 "name": "Inca City"
55 }
55 }
56 },
56 },
57 url_template="http://s3-eu-west-1.amazonaws.com/whereonmars.cartodb.net/{basemap_id}/{z}/{x}/{y}.png",
57 url_template="http://s3-eu-west-1.amazonaws.com/whereonmars.cartodb.net/{basemap_id}/{z}/{x}/{y}.png",
58 layer_options={
58 layer_options={
59 "basemap_id": "celestia_mars-shaded-16k_global",
59 "basemap_id": "celestia_mars-shaded-16k_global",
60 "attribution": "Celestia/praesepe",
60 "attribution": "Celestia/praesepe",
61 "minZoom": 0,
61 "minZoom": 0,
62 "maxZoom": 18,
62 "maxZoom": 18,
63 },
63 },
64 )
64 )
65 assert "<IPython.core.display.GeoJSON object>" == str(gj)
65 assert "<IPython.core.display.GeoJSON object>" == str(gj)
66
66
67
67
68 def test_retina_png():
68 def test_retina_png():
69 here = os.path.dirname(__file__)
69 here = os.path.dirname(__file__)
70 img = display.Image(os.path.join(here, "2x2.png"), retina=True)
70 img = display.Image(os.path.join(here, "2x2.png"), retina=True)
71 assert img.height == 1
71 assert img.height == 1
72 assert img.width == 1
72 assert img.width == 1
73 data, md = img._repr_png_()
73 data, md = img._repr_png_()
74 assert md["width"] == 1
74 assert md["width"] == 1
75 assert md["height"] == 1
75 assert md["height"] == 1
76
76
77
77
78 def test_embed_svg_url():
78 def test_embed_svg_url():
79 import gzip
79 import gzip
80 from io import BytesIO
80 from io import BytesIO
81 svg_data = b'<svg><circle x="0" y="0" r="1"/></svg>'
81 svg_data = b'<svg><circle x="0" y="0" r="1"/></svg>'
82 url = 'http://test.com/circle.svg'
82 url = 'http://test.com/circle.svg'
83
83
84 gzip_svg = BytesIO()
84 gzip_svg = BytesIO()
85 with gzip.open(gzip_svg, 'wb') as fp:
85 with gzip.open(gzip_svg, 'wb') as fp:
86 fp.write(svg_data)
86 fp.write(svg_data)
87 gzip_svg = gzip_svg.getvalue()
87 gzip_svg = gzip_svg.getvalue()
88
88
89 def mocked_urlopen(*args, **kwargs):
89 def mocked_urlopen(*args, **kwargs):
90 class MockResponse:
90 class MockResponse:
91 def __init__(self, svg):
91 def __init__(self, svg):
92 self._svg_data = svg
92 self._svg_data = svg
93 self.headers = {'content-type': 'image/svg+xml'}
93 self.headers = {'content-type': 'image/svg+xml'}
94
94
95 def read(self):
95 def read(self):
96 return self._svg_data
96 return self._svg_data
97
97
98 if args[0] == url:
98 if args[0] == url:
99 return MockResponse(svg_data)
99 return MockResponse(svg_data)
100 elif args[0] == url + "z":
100 elif args[0] == url + "z":
101 ret = MockResponse(gzip_svg)
101 ret = MockResponse(gzip_svg)
102 ret.headers["content-encoding"] = "gzip"
102 ret.headers["content-encoding"] = "gzip"
103 return ret
103 return ret
104 return MockResponse(None)
104 return MockResponse(None)
105
105
106 with mock.patch('urllib.request.urlopen', side_effect=mocked_urlopen):
106 with mock.patch('urllib.request.urlopen', side_effect=mocked_urlopen):
107 svg = display.SVG(url=url)
107 svg = display.SVG(url=url)
108 assert svg._repr_svg_().startswith("<svg") is True
108 assert svg._repr_svg_().startswith("<svg") is True
109 svg = display.SVG(url=url + "z")
109 svg = display.SVG(url=url + "z")
110 assert svg._repr_svg_().startswith("<svg") is True
110 assert svg._repr_svg_().startswith("<svg") is True
111
111
112
112
113 def test_retina_jpeg():
113 def test_retina_jpeg():
114 here = os.path.dirname(__file__)
114 here = os.path.dirname(__file__)
115 img = display.Image(os.path.join(here, "2x2.jpg"), retina=True)
115 img = display.Image(os.path.join(here, "2x2.jpg"), retina=True)
116 assert img.height == 1
116 assert img.height == 1
117 assert img.width == 1
117 assert img.width == 1
118 data, md = img._repr_jpeg_()
118 data, md = img._repr_jpeg_()
119 assert md["width"] == 1
119 assert md["width"] == 1
120 assert md["height"] == 1
120 assert md["height"] == 1
121
121
122
122
123 def test_base64image():
123 def test_base64image():
124 display.Image("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEUAAACnej3aAAAAAWJLR0QAiAUdSAAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB94BCRQnOqNu0b4AAAAKSURBVAjXY2AAAAACAAHiIbwzAAAAAElFTkSuQmCC")
124 display.Image("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEUAAACnej3aAAAAAWJLR0QAiAUdSAAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB94BCRQnOqNu0b4AAAAKSURBVAjXY2AAAAACAAHiIbwzAAAAAElFTkSuQmCC")
125
125
126 def test_image_filename_defaults():
126 def test_image_filename_defaults():
127 '''test format constraint, and validity of jpeg and png'''
127 '''test format constraint, and validity of jpeg and png'''
128 tpath = ipath.get_ipython_package_dir()
128 tpath = ipath.get_ipython_package_dir()
129 pytest.raises(
129 pytest.raises(
130 ValueError,
130 ValueError,
131 display.Image,
131 display.Image,
132 filename=os.path.join(tpath, "testing/tests/badformat.zip"),
132 filename=os.path.join(tpath, "testing/tests/badformat.zip"),
133 embed=True,
133 embed=True,
134 )
134 )
135 pytest.raises(ValueError, display.Image)
135 pytest.raises(ValueError, display.Image)
136 pytest.raises(
136 pytest.raises(
137 ValueError,
137 ValueError,
138 display.Image,
138 display.Image,
139 data="this is not an image",
139 data="this is not an image",
140 format="badformat",
140 format="badformat",
141 embed=True,
141 embed=True,
142 )
142 )
143 # check boths paths to allow packages to test at build and install time
143 # check both paths to allow packages to test at build and install time
144 imgfile = os.path.join(tpath, 'core/tests/2x2.png')
144 imgfile = os.path.join(tpath, 'core/tests/2x2.png')
145 img = display.Image(filename=imgfile)
145 img = display.Image(filename=imgfile)
146 assert "png" == img.format
146 assert "png" == img.format
147 assert img._repr_png_() is not None
147 assert img._repr_png_() is not None
148 img = display.Image(
148 img = display.Image(
149 filename=os.path.join(tpath, "testing/tests/logo.jpg"), embed=False
149 filename=os.path.join(tpath, "testing/tests/logo.jpg"), embed=False
150 )
150 )
151 assert "jpeg" == img.format
151 assert "jpeg" == img.format
152 assert img._repr_jpeg_() is None
152 assert img._repr_jpeg_() is None
153
153
154 def _get_inline_config():
154 def _get_inline_config():
155 from matplotlib_inline.config import InlineBackend
155 from matplotlib_inline.config import InlineBackend
156 return InlineBackend.instance()
156 return InlineBackend.instance()
157
157
158
158
159 @dec.skip_without("matplotlib")
159 @dec.skip_without("matplotlib")
160 def test_set_matplotlib_close():
160 def test_set_matplotlib_close():
161 cfg = _get_inline_config()
161 cfg = _get_inline_config()
162 cfg.close_figures = False
162 cfg.close_figures = False
163 with pytest.deprecated_call():
163 with pytest.deprecated_call():
164 display.set_matplotlib_close()
164 display.set_matplotlib_close()
165 assert cfg.close_figures
165 assert cfg.close_figures
166 with pytest.deprecated_call():
166 with pytest.deprecated_call():
167 display.set_matplotlib_close(False)
167 display.set_matplotlib_close(False)
168 assert not cfg.close_figures
168 assert not cfg.close_figures
169
169
170 _fmt_mime_map = {
170 _fmt_mime_map = {
171 'png': 'image/png',
171 'png': 'image/png',
172 'jpeg': 'image/jpeg',
172 'jpeg': 'image/jpeg',
173 'pdf': 'application/pdf',
173 'pdf': 'application/pdf',
174 'retina': 'image/png',
174 'retina': 'image/png',
175 'svg': 'image/svg+xml',
175 'svg': 'image/svg+xml',
176 }
176 }
177
177
178 @dec.skip_without('matplotlib')
178 @dec.skip_without('matplotlib')
179 def test_set_matplotlib_formats():
179 def test_set_matplotlib_formats():
180 from matplotlib.figure import Figure
180 from matplotlib.figure import Figure
181 formatters = get_ipython().display_formatter.formatters
181 formatters = get_ipython().display_formatter.formatters
182 for formats in [
182 for formats in [
183 ('png',),
183 ('png',),
184 ('pdf', 'svg'),
184 ('pdf', 'svg'),
185 ('jpeg', 'retina', 'png'),
185 ('jpeg', 'retina', 'png'),
186 (),
186 (),
187 ]:
187 ]:
188 active_mimes = {_fmt_mime_map[fmt] for fmt in formats}
188 active_mimes = {_fmt_mime_map[fmt] for fmt in formats}
189 with pytest.deprecated_call():
189 with pytest.deprecated_call():
190 display.set_matplotlib_formats(*formats)
190 display.set_matplotlib_formats(*formats)
191 for mime, f in formatters.items():
191 for mime, f in formatters.items():
192 if mime in active_mimes:
192 if mime in active_mimes:
193 assert Figure in f
193 assert Figure in f
194 else:
194 else:
195 assert Figure not in f
195 assert Figure not in f
196
196
197
197
198 @dec.skip_without("matplotlib")
198 @dec.skip_without("matplotlib")
199 def test_set_matplotlib_formats_kwargs():
199 def test_set_matplotlib_formats_kwargs():
200 from matplotlib.figure import Figure
200 from matplotlib.figure import Figure
201 ip = get_ipython()
201 ip = get_ipython()
202 cfg = _get_inline_config()
202 cfg = _get_inline_config()
203 cfg.print_figure_kwargs.update(dict(foo='bar'))
203 cfg.print_figure_kwargs.update(dict(foo='bar'))
204 kwargs = dict(dpi=150)
204 kwargs = dict(dpi=150)
205 with pytest.deprecated_call():
205 with pytest.deprecated_call():
206 display.set_matplotlib_formats("png", **kwargs)
206 display.set_matplotlib_formats("png", **kwargs)
207 formatter = ip.display_formatter.formatters["image/png"]
207 formatter = ip.display_formatter.formatters["image/png"]
208 f = formatter.lookup_by_type(Figure)
208 f = formatter.lookup_by_type(Figure)
209 formatter_kwargs = f.keywords
209 formatter_kwargs = f.keywords
210 expected = kwargs
210 expected = kwargs
211 expected["base64"] = True
211 expected["base64"] = True
212 expected["fmt"] = "png"
212 expected["fmt"] = "png"
213 expected.update(cfg.print_figure_kwargs)
213 expected.update(cfg.print_figure_kwargs)
214 assert formatter_kwargs == expected
214 assert formatter_kwargs == expected
215
215
216 def test_display_available():
216 def test_display_available():
217 """
217 """
218 Test that display is available without import
218 Test that display is available without import
219
219
220 We don't really care if it's in builtin or anything else, but it should
220 We don't really care if it's in builtin or anything else, but it should
221 always be available.
221 always be available.
222 """
222 """
223 ip = get_ipython()
223 ip = get_ipython()
224 with AssertNotPrints('NameError'):
224 with AssertNotPrints('NameError'):
225 ip.run_cell('display')
225 ip.run_cell('display')
226 try:
226 try:
227 ip.run_cell('del display')
227 ip.run_cell('del display')
228 except NameError:
228 except NameError:
229 pass # it's ok, it might be in builtins
229 pass # it's ok, it might be in builtins
230 # even if deleted it should be back
230 # even if deleted it should be back
231 with AssertNotPrints('NameError'):
231 with AssertNotPrints('NameError'):
232 ip.run_cell('display')
232 ip.run_cell('display')
233
233
234 def test_textdisplayobj_pretty_repr():
234 def test_textdisplayobj_pretty_repr():
235 p = display.Pretty("This is a simple test")
235 p = display.Pretty("This is a simple test")
236 assert repr(p) == "<IPython.core.display.Pretty object>"
236 assert repr(p) == "<IPython.core.display.Pretty object>"
237 assert p.data == "This is a simple test"
237 assert p.data == "This is a simple test"
238
238
239 p._show_mem_addr = True
239 p._show_mem_addr = True
240 assert repr(p) == object.__repr__(p)
240 assert repr(p) == object.__repr__(p)
241
241
242
242
243 def test_displayobject_repr():
243 def test_displayobject_repr():
244 h = display.HTML("<br />")
244 h = display.HTML("<br />")
245 assert repr(h) == "<IPython.core.display.HTML object>"
245 assert repr(h) == "<IPython.core.display.HTML object>"
246 h._show_mem_addr = True
246 h._show_mem_addr = True
247 assert repr(h) == object.__repr__(h)
247 assert repr(h) == object.__repr__(h)
248 h._show_mem_addr = False
248 h._show_mem_addr = False
249 assert repr(h) == "<IPython.core.display.HTML object>"
249 assert repr(h) == "<IPython.core.display.HTML object>"
250
250
251 j = display.Javascript("")
251 j = display.Javascript("")
252 assert repr(j) == "<IPython.core.display.Javascript object>"
252 assert repr(j) == "<IPython.core.display.Javascript object>"
253 j._show_mem_addr = True
253 j._show_mem_addr = True
254 assert repr(j) == object.__repr__(j)
254 assert repr(j) == object.__repr__(j)
255 j._show_mem_addr = False
255 j._show_mem_addr = False
256 assert repr(j) == "<IPython.core.display.Javascript object>"
256 assert repr(j) == "<IPython.core.display.Javascript object>"
257
257
258 @mock.patch('warnings.warn')
258 @mock.patch('warnings.warn')
259 def test_encourage_iframe_over_html(m_warn):
259 def test_encourage_iframe_over_html(m_warn):
260 display.HTML()
260 display.HTML()
261 m_warn.assert_not_called()
261 m_warn.assert_not_called()
262
262
263 display.HTML('<br />')
263 display.HTML('<br />')
264 m_warn.assert_not_called()
264 m_warn.assert_not_called()
265
265
266 display.HTML('<html><p>Lots of content here</p><iframe src="http://a.com"></iframe>')
266 display.HTML('<html><p>Lots of content here</p><iframe src="http://a.com"></iframe>')
267 m_warn.assert_not_called()
267 m_warn.assert_not_called()
268
268
269 display.HTML('<iframe src="http://a.com"></iframe>')
269 display.HTML('<iframe src="http://a.com"></iframe>')
270 m_warn.assert_called_with('Consider using IPython.display.IFrame instead')
270 m_warn.assert_called_with('Consider using IPython.display.IFrame instead')
271
271
272 m_warn.reset_mock()
272 m_warn.reset_mock()
273 display.HTML('<IFRAME SRC="http://a.com"></IFRAME>')
273 display.HTML('<IFRAME SRC="http://a.com"></IFRAME>')
274 m_warn.assert_called_with('Consider using IPython.display.IFrame instead')
274 m_warn.assert_called_with('Consider using IPython.display.IFrame instead')
275
275
276 def test_progress():
276 def test_progress():
277 p = display.ProgressBar(10)
277 p = display.ProgressBar(10)
278 assert "0/10" in repr(p)
278 assert "0/10" in repr(p)
279 p.html_width = "100%"
279 p.html_width = "100%"
280 p.progress = 5
280 p.progress = 5
281 assert (
281 assert (
282 p._repr_html_() == "<progress style='width:100%' max='10' value='5'></progress>"
282 p._repr_html_() == "<progress style='width:100%' max='10' value='5'></progress>"
283 )
283 )
284
284
285
285
286 def test_progress_iter():
286 def test_progress_iter():
287 with capture_output(display=False) as captured:
287 with capture_output(display=False) as captured:
288 for i in display.ProgressBar(5):
288 for i in display.ProgressBar(5):
289 out = captured.stdout
289 out = captured.stdout
290 assert "{0}/5".format(i) in out
290 assert "{0}/5".format(i) in out
291 out = captured.stdout
291 out = captured.stdout
292 assert "5/5" in out
292 assert "5/5" in out
293
293
294
294
295 def test_json():
295 def test_json():
296 d = {'a': 5}
296 d = {'a': 5}
297 lis = [d]
297 lis = [d]
298 metadata = [
298 metadata = [
299 {'expanded': False, 'root': 'root'},
299 {'expanded': False, 'root': 'root'},
300 {'expanded': True, 'root': 'root'},
300 {'expanded': True, 'root': 'root'},
301 {'expanded': False, 'root': 'custom'},
301 {'expanded': False, 'root': 'custom'},
302 {'expanded': True, 'root': 'custom'},
302 {'expanded': True, 'root': 'custom'},
303 ]
303 ]
304 json_objs = [
304 json_objs = [
305 display.JSON(d),
305 display.JSON(d),
306 display.JSON(d, expanded=True),
306 display.JSON(d, expanded=True),
307 display.JSON(d, root='custom'),
307 display.JSON(d, root='custom'),
308 display.JSON(d, expanded=True, root='custom'),
308 display.JSON(d, expanded=True, root='custom'),
309 ]
309 ]
310 for j, md in zip(json_objs, metadata):
310 for j, md in zip(json_objs, metadata):
311 assert j._repr_json_() == (d, md)
311 assert j._repr_json_() == (d, md)
312
312
313 with warnings.catch_warnings(record=True) as w:
313 with warnings.catch_warnings(record=True) as w:
314 warnings.simplefilter("always")
314 warnings.simplefilter("always")
315 j = display.JSON(json.dumps(d))
315 j = display.JSON(json.dumps(d))
316 assert len(w) == 1
316 assert len(w) == 1
317 assert j._repr_json_() == (d, metadata[0])
317 assert j._repr_json_() == (d, metadata[0])
318
318
319 json_objs = [
319 json_objs = [
320 display.JSON(lis),
320 display.JSON(lis),
321 display.JSON(lis, expanded=True),
321 display.JSON(lis, expanded=True),
322 display.JSON(lis, root='custom'),
322 display.JSON(lis, root='custom'),
323 display.JSON(lis, expanded=True, root='custom'),
323 display.JSON(lis, expanded=True, root='custom'),
324 ]
324 ]
325 for j, md in zip(json_objs, metadata):
325 for j, md in zip(json_objs, metadata):
326 assert j._repr_json_() == (lis, md)
326 assert j._repr_json_() == (lis, md)
327
327
328 with warnings.catch_warnings(record=True) as w:
328 with warnings.catch_warnings(record=True) as w:
329 warnings.simplefilter("always")
329 warnings.simplefilter("always")
330 j = display.JSON(json.dumps(lis))
330 j = display.JSON(json.dumps(lis))
331 assert len(w) == 1
331 assert len(w) == 1
332 assert j._repr_json_() == (lis, metadata[0])
332 assert j._repr_json_() == (lis, metadata[0])
333
333
334
334
335 def test_video_embedding():
335 def test_video_embedding():
336 """use a tempfile, with dummy-data, to ensure that video embedding doesn't crash"""
336 """use a tempfile, with dummy-data, to ensure that video embedding doesn't crash"""
337 v = display.Video("http://ignored")
337 v = display.Video("http://ignored")
338 assert not v.embed
338 assert not v.embed
339 html = v._repr_html_()
339 html = v._repr_html_()
340 assert 'src="data:' not in html
340 assert 'src="data:' not in html
341 assert 'src="http://ignored"' in html
341 assert 'src="http://ignored"' in html
342
342
343 with pytest.raises(ValueError):
343 with pytest.raises(ValueError):
344 v = display.Video(b'abc')
344 v = display.Video(b'abc')
345
345
346 with NamedFileInTemporaryDirectory('test.mp4') as f:
346 with NamedFileInTemporaryDirectory('test.mp4') as f:
347 f.write(b'abc')
347 f.write(b'abc')
348 f.close()
348 f.close()
349
349
350 v = display.Video(f.name)
350 v = display.Video(f.name)
351 assert not v.embed
351 assert not v.embed
352 html = v._repr_html_()
352 html = v._repr_html_()
353 assert 'src="data:' not in html
353 assert 'src="data:' not in html
354
354
355 v = display.Video(f.name, embed=True)
355 v = display.Video(f.name, embed=True)
356 html = v._repr_html_()
356 html = v._repr_html_()
357 assert 'src="data:video/mp4;base64,YWJj"' in html
357 assert 'src="data:video/mp4;base64,YWJj"' in html
358
358
359 v = display.Video(f.name, embed=True, mimetype='video/other')
359 v = display.Video(f.name, embed=True, mimetype='video/other')
360 html = v._repr_html_()
360 html = v._repr_html_()
361 assert 'src="data:video/other;base64,YWJj"' in html
361 assert 'src="data:video/other;base64,YWJj"' in html
362
362
363 v = display.Video(b'abc', embed=True, mimetype='video/mp4')
363 v = display.Video(b'abc', embed=True, mimetype='video/mp4')
364 html = v._repr_html_()
364 html = v._repr_html_()
365 assert 'src="data:video/mp4;base64,YWJj"' in html
365 assert 'src="data:video/mp4;base64,YWJj"' in html
366
366
367 v = display.Video(u'YWJj', embed=True, mimetype='video/xyz')
367 v = display.Video(u'YWJj', embed=True, mimetype='video/xyz')
368 html = v._repr_html_()
368 html = v._repr_html_()
369 assert 'src="data:video/xyz;base64,YWJj"' in html
369 assert 'src="data:video/xyz;base64,YWJj"' in html
370
370
371 def test_html_metadata():
371 def test_html_metadata():
372 s = "<h1>Test</h1>"
372 s = "<h1>Test</h1>"
373 h = display.HTML(s, metadata={"isolated": True})
373 h = display.HTML(s, metadata={"isolated": True})
374 assert h._repr_html_() == (s, {"isolated": True})
374 assert h._repr_html_() == (s, {"isolated": True})
375
375
376
376
377 def test_display_id():
377 def test_display_id():
378 ip = get_ipython()
378 ip = get_ipython()
379 with mock.patch.object(ip.display_pub, 'publish') as pub:
379 with mock.patch.object(ip.display_pub, 'publish') as pub:
380 handle = display.display('x')
380 handle = display.display('x')
381 assert handle is None
381 assert handle is None
382 handle = display.display('y', display_id='secret')
382 handle = display.display('y', display_id='secret')
383 assert isinstance(handle, display.DisplayHandle)
383 assert isinstance(handle, display.DisplayHandle)
384 handle2 = display.display('z', display_id=True)
384 handle2 = display.display('z', display_id=True)
385 assert isinstance(handle2, display.DisplayHandle)
385 assert isinstance(handle2, display.DisplayHandle)
386 assert handle.display_id != handle2.display_id
386 assert handle.display_id != handle2.display_id
387
387
388 assert pub.call_count == 3
388 assert pub.call_count == 3
389 args, kwargs = pub.call_args_list[0]
389 args, kwargs = pub.call_args_list[0]
390 assert args == ()
390 assert args == ()
391 assert kwargs == {
391 assert kwargs == {
392 'data': {
392 'data': {
393 'text/plain': repr('x')
393 'text/plain': repr('x')
394 },
394 },
395 'metadata': {},
395 'metadata': {},
396 }
396 }
397 args, kwargs = pub.call_args_list[1]
397 args, kwargs = pub.call_args_list[1]
398 assert args == ()
398 assert args == ()
399 assert kwargs == {
399 assert kwargs == {
400 'data': {
400 'data': {
401 'text/plain': repr('y')
401 'text/plain': repr('y')
402 },
402 },
403 'metadata': {},
403 'metadata': {},
404 'transient': {
404 'transient': {
405 'display_id': handle.display_id,
405 'display_id': handle.display_id,
406 },
406 },
407 }
407 }
408 args, kwargs = pub.call_args_list[2]
408 args, kwargs = pub.call_args_list[2]
409 assert args == ()
409 assert args == ()
410 assert kwargs == {
410 assert kwargs == {
411 'data': {
411 'data': {
412 'text/plain': repr('z')
412 'text/plain': repr('z')
413 },
413 },
414 'metadata': {},
414 'metadata': {},
415 'transient': {
415 'transient': {
416 'display_id': handle2.display_id,
416 'display_id': handle2.display_id,
417 },
417 },
418 }
418 }
419
419
420
420
421 def test_update_display():
421 def test_update_display():
422 ip = get_ipython()
422 ip = get_ipython()
423 with mock.patch.object(ip.display_pub, 'publish') as pub:
423 with mock.patch.object(ip.display_pub, 'publish') as pub:
424 with pytest.raises(TypeError):
424 with pytest.raises(TypeError):
425 display.update_display('x')
425 display.update_display('x')
426 display.update_display('x', display_id='1')
426 display.update_display('x', display_id='1')
427 display.update_display('y', display_id='2')
427 display.update_display('y', display_id='2')
428 args, kwargs = pub.call_args_list[0]
428 args, kwargs = pub.call_args_list[0]
429 assert args == ()
429 assert args == ()
430 assert kwargs == {
430 assert kwargs == {
431 'data': {
431 'data': {
432 'text/plain': repr('x')
432 'text/plain': repr('x')
433 },
433 },
434 'metadata': {},
434 'metadata': {},
435 'transient': {
435 'transient': {
436 'display_id': '1',
436 'display_id': '1',
437 },
437 },
438 'update': True,
438 'update': True,
439 }
439 }
440 args, kwargs = pub.call_args_list[1]
440 args, kwargs = pub.call_args_list[1]
441 assert args == ()
441 assert args == ()
442 assert kwargs == {
442 assert kwargs == {
443 'data': {
443 'data': {
444 'text/plain': repr('y')
444 'text/plain': repr('y')
445 },
445 },
446 'metadata': {},
446 'metadata': {},
447 'transient': {
447 'transient': {
448 'display_id': '2',
448 'display_id': '2',
449 },
449 },
450 'update': True,
450 'update': True,
451 }
451 }
452
452
453
453
454 def test_display_handle():
454 def test_display_handle():
455 ip = get_ipython()
455 ip = get_ipython()
456 handle = display.DisplayHandle()
456 handle = display.DisplayHandle()
457 assert isinstance(handle.display_id, str)
457 assert isinstance(handle.display_id, str)
458 handle = display.DisplayHandle("my-id")
458 handle = display.DisplayHandle("my-id")
459 assert handle.display_id == "my-id"
459 assert handle.display_id == "my-id"
460 with mock.patch.object(ip.display_pub, "publish") as pub:
460 with mock.patch.object(ip.display_pub, "publish") as pub:
461 handle.display("x")
461 handle.display("x")
462 handle.update("y")
462 handle.update("y")
463
463
464 args, kwargs = pub.call_args_list[0]
464 args, kwargs = pub.call_args_list[0]
465 assert args == ()
465 assert args == ()
466 assert kwargs == {
466 assert kwargs == {
467 'data': {
467 'data': {
468 'text/plain': repr('x')
468 'text/plain': repr('x')
469 },
469 },
470 'metadata': {},
470 'metadata': {},
471 'transient': {
471 'transient': {
472 'display_id': handle.display_id,
472 'display_id': handle.display_id,
473 }
473 }
474 }
474 }
475 args, kwargs = pub.call_args_list[1]
475 args, kwargs = pub.call_args_list[1]
476 assert args == ()
476 assert args == ()
477 assert kwargs == {
477 assert kwargs == {
478 'data': {
478 'data': {
479 'text/plain': repr('y')
479 'text/plain': repr('y')
480 },
480 },
481 'metadata': {},
481 'metadata': {},
482 'transient': {
482 'transient': {
483 'display_id': handle.display_id,
483 'display_id': handle.display_id,
484 },
484 },
485 'update': True,
485 'update': True,
486 }
486 }
487
487
488
488
489 def test_image_alt_tag():
489 def test_image_alt_tag():
490 """Simple test for display.Image(args, alt=x,)"""
490 """Simple test for display.Image(args, alt=x,)"""
491 thisurl = "http://example.com/image.png"
491 thisurl = "http://example.com/image.png"
492 img = display.Image(url=thisurl, alt="an image")
492 img = display.Image(url=thisurl, alt="an image")
493 assert '<img src="%s" alt="an image"/>' % (thisurl) == img._repr_html_()
493 assert '<img src="%s" alt="an image"/>' % (thisurl) == img._repr_html_()
494 img = display.Image(url=thisurl, unconfined=True, alt="an image")
494 img = display.Image(url=thisurl, unconfined=True, alt="an image")
495 assert (
495 assert (
496 '<img src="%s" class="unconfined" alt="an image"/>' % (thisurl)
496 '<img src="%s" class="unconfined" alt="an image"/>' % (thisurl)
497 == img._repr_html_()
497 == img._repr_html_()
498 )
498 )
499 img = display.Image(url=thisurl, alt='>"& <')
499 img = display.Image(url=thisurl, alt='>"& <')
500 assert '<img src="%s" alt="&gt;&quot;&amp; &lt;"/>' % (thisurl) == img._repr_html_()
500 assert '<img src="%s" alt="&gt;&quot;&amp; &lt;"/>' % (thisurl) == img._repr_html_()
501
501
502 img = display.Image(url=thisurl, metadata={"alt": "an image"})
502 img = display.Image(url=thisurl, metadata={"alt": "an image"})
503 assert img.alt == "an image"
503 assert img.alt == "an image"
504 here = os.path.dirname(__file__)
504 here = os.path.dirname(__file__)
505 img = display.Image(os.path.join(here, "2x2.png"), alt="an image")
505 img = display.Image(os.path.join(here, "2x2.png"), alt="an image")
506 assert img.alt == "an image"
506 assert img.alt == "an image"
507 _, md = img._repr_png_()
507 _, md = img._repr_png_()
508 assert md["alt"] == "an image"
508 assert md["alt"] == "an image"
509
509
510
510
511 def test_image_bad_filename_raises_proper_exception():
511 def test_image_bad_filename_raises_proper_exception():
512 with pytest.raises(FileNotFoundError):
512 with pytest.raises(FileNotFoundError):
513 display.Image("/this/file/does/not/exist/")._repr_png_()
513 display.Image("/this/file/does/not/exist/")._repr_png_()
@@ -1,1221 +1,1221
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """Tests for the key interactiveshell module.
2 """Tests for the key interactiveshell module.
3
3
4 Historically the main classes in interactiveshell have been under-tested. This
4 Historically the main classes in interactiveshell have been under-tested. This
5 module should grow as many single-method tests as possible to trap many of the
5 module should grow as many single-method tests as possible to trap many of the
6 recurring bugs we seem to encounter with high-level interaction.
6 recurring bugs we seem to encounter with high-level interaction.
7 """
7 """
8
8
9 # Copyright (c) IPython Development Team.
9 # Copyright (c) IPython Development Team.
10 # Distributed under the terms of the Modified BSD License.
10 # Distributed under the terms of the Modified BSD License.
11
11
12 import asyncio
12 import asyncio
13 import ast
13 import ast
14 import os
14 import os
15 import signal
15 import signal
16 import shutil
16 import shutil
17 import sys
17 import sys
18 import tempfile
18 import tempfile
19 import unittest
19 import unittest
20 import pytest
20 import pytest
21 from unittest import mock
21 from unittest import mock
22
22
23 from os.path import join
23 from os.path import join
24
24
25 from IPython.core.error import InputRejected
25 from IPython.core.error import InputRejected
26 from IPython.core.inputtransformer import InputTransformer
26 from IPython.core.inputtransformer import InputTransformer
27 from IPython.core import interactiveshell
27 from IPython.core import interactiveshell
28 from IPython.core.oinspect import OInfo
28 from IPython.core.oinspect import OInfo
29 from IPython.testing.decorators import (
29 from IPython.testing.decorators import (
30 skipif,
30 skipif,
31 skip_win32,
31 skip_win32,
32 onlyif_unicode_paths,
32 onlyif_unicode_paths,
33 onlyif_cmds_exist,
33 onlyif_cmds_exist,
34 skip_if_not_osx,
34 skip_if_not_osx,
35 )
35 )
36 from IPython.testing import tools as tt
36 from IPython.testing import tools as tt
37 from IPython.utils.process import find_cmd
37 from IPython.utils.process import find_cmd
38
38
39 #-----------------------------------------------------------------------------
39 #-----------------------------------------------------------------------------
40 # Globals
40 # Globals
41 #-----------------------------------------------------------------------------
41 #-----------------------------------------------------------------------------
42 # This is used by every single test, no point repeating it ad nauseam
42 # This is used by every single test, no point repeating it ad nauseam
43
43
44 #-----------------------------------------------------------------------------
44 #-----------------------------------------------------------------------------
45 # Tests
45 # Tests
46 #-----------------------------------------------------------------------------
46 #-----------------------------------------------------------------------------
47
47
48 class DerivedInterrupt(KeyboardInterrupt):
48 class DerivedInterrupt(KeyboardInterrupt):
49 pass
49 pass
50
50
51 class InteractiveShellTestCase(unittest.TestCase):
51 class InteractiveShellTestCase(unittest.TestCase):
52 def test_naked_string_cells(self):
52 def test_naked_string_cells(self):
53 """Test that cells with only naked strings are fully executed"""
53 """Test that cells with only naked strings are fully executed"""
54 # First, single-line inputs
54 # First, single-line inputs
55 ip.run_cell('"a"\n')
55 ip.run_cell('"a"\n')
56 self.assertEqual(ip.user_ns['_'], 'a')
56 self.assertEqual(ip.user_ns['_'], 'a')
57 # And also multi-line cells
57 # And also multi-line cells
58 ip.run_cell('"""a\nb"""\n')
58 ip.run_cell('"""a\nb"""\n')
59 self.assertEqual(ip.user_ns['_'], 'a\nb')
59 self.assertEqual(ip.user_ns['_'], 'a\nb')
60
60
61 def test_run_empty_cell(self):
61 def test_run_empty_cell(self):
62 """Just make sure we don't get a horrible error with a blank
62 """Just make sure we don't get a horrible error with a blank
63 cell of input. Yes, I did overlook that."""
63 cell of input. Yes, I did overlook that."""
64 old_xc = ip.execution_count
64 old_xc = ip.execution_count
65 res = ip.run_cell('')
65 res = ip.run_cell('')
66 self.assertEqual(ip.execution_count, old_xc)
66 self.assertEqual(ip.execution_count, old_xc)
67 self.assertEqual(res.execution_count, None)
67 self.assertEqual(res.execution_count, None)
68
68
69 def test_run_cell_multiline(self):
69 def test_run_cell_multiline(self):
70 """Multi-block, multi-line cells must execute correctly.
70 """Multi-block, multi-line cells must execute correctly.
71 """
71 """
72 src = '\n'.join(["x=1",
72 src = '\n'.join(["x=1",
73 "y=2",
73 "y=2",
74 "if 1:",
74 "if 1:",
75 " x += 1",
75 " x += 1",
76 " y += 1",])
76 " y += 1",])
77 res = ip.run_cell(src)
77 res = ip.run_cell(src)
78 self.assertEqual(ip.user_ns['x'], 2)
78 self.assertEqual(ip.user_ns['x'], 2)
79 self.assertEqual(ip.user_ns['y'], 3)
79 self.assertEqual(ip.user_ns['y'], 3)
80 self.assertEqual(res.success, True)
80 self.assertEqual(res.success, True)
81 self.assertEqual(res.result, None)
81 self.assertEqual(res.result, None)
82
82
83 def test_multiline_string_cells(self):
83 def test_multiline_string_cells(self):
84 "Code sprinkled with multiline strings should execute (GH-306)"
84 "Code sprinkled with multiline strings should execute (GH-306)"
85 ip.run_cell('tmp=0')
85 ip.run_cell('tmp=0')
86 self.assertEqual(ip.user_ns['tmp'], 0)
86 self.assertEqual(ip.user_ns['tmp'], 0)
87 res = ip.run_cell('tmp=1;"""a\nb"""\n')
87 res = ip.run_cell('tmp=1;"""a\nb"""\n')
88 self.assertEqual(ip.user_ns['tmp'], 1)
88 self.assertEqual(ip.user_ns['tmp'], 1)
89 self.assertEqual(res.success, True)
89 self.assertEqual(res.success, True)
90 self.assertEqual(res.result, "a\nb")
90 self.assertEqual(res.result, "a\nb")
91
91
92 def test_dont_cache_with_semicolon(self):
92 def test_dont_cache_with_semicolon(self):
93 "Ending a line with semicolon should not cache the returned object (GH-307)"
93 "Ending a line with semicolon should not cache the returned object (GH-307)"
94 oldlen = len(ip.user_ns['Out'])
94 oldlen = len(ip.user_ns['Out'])
95 for cell in ['1;', '1;1;']:
95 for cell in ['1;', '1;1;']:
96 res = ip.run_cell(cell, store_history=True)
96 res = ip.run_cell(cell, store_history=True)
97 newlen = len(ip.user_ns['Out'])
97 newlen = len(ip.user_ns['Out'])
98 self.assertEqual(oldlen, newlen)
98 self.assertEqual(oldlen, newlen)
99 self.assertIsNone(res.result)
99 self.assertIsNone(res.result)
100 i = 0
100 i = 0
101 #also test the default caching behavior
101 #also test the default caching behavior
102 for cell in ['1', '1;1']:
102 for cell in ['1', '1;1']:
103 ip.run_cell(cell, store_history=True)
103 ip.run_cell(cell, store_history=True)
104 newlen = len(ip.user_ns['Out'])
104 newlen = len(ip.user_ns['Out'])
105 i += 1
105 i += 1
106 self.assertEqual(oldlen+i, newlen)
106 self.assertEqual(oldlen+i, newlen)
107
107
108 def test_syntax_error(self):
108 def test_syntax_error(self):
109 res = ip.run_cell("raise = 3")
109 res = ip.run_cell("raise = 3")
110 self.assertIsInstance(res.error_before_exec, SyntaxError)
110 self.assertIsInstance(res.error_before_exec, SyntaxError)
111
111
112 def test_open_standard_input_stream(self):
112 def test_open_standard_input_stream(self):
113 res = ip.run_cell("open(0)")
113 res = ip.run_cell("open(0)")
114 self.assertIsInstance(res.error_in_exec, ValueError)
114 self.assertIsInstance(res.error_in_exec, ValueError)
115
115
116 def test_open_standard_output_stream(self):
116 def test_open_standard_output_stream(self):
117 res = ip.run_cell("open(1)")
117 res = ip.run_cell("open(1)")
118 self.assertIsInstance(res.error_in_exec, ValueError)
118 self.assertIsInstance(res.error_in_exec, ValueError)
119
119
120 def test_open_standard_error_stream(self):
120 def test_open_standard_error_stream(self):
121 res = ip.run_cell("open(2)")
121 res = ip.run_cell("open(2)")
122 self.assertIsInstance(res.error_in_exec, ValueError)
122 self.assertIsInstance(res.error_in_exec, ValueError)
123
123
124 def test_In_variable(self):
124 def test_In_variable(self):
125 "Verify that In variable grows with user input (GH-284)"
125 "Verify that In variable grows with user input (GH-284)"
126 oldlen = len(ip.user_ns['In'])
126 oldlen = len(ip.user_ns['In'])
127 ip.run_cell('1;', store_history=True)
127 ip.run_cell('1;', store_history=True)
128 newlen = len(ip.user_ns['In'])
128 newlen = len(ip.user_ns['In'])
129 self.assertEqual(oldlen+1, newlen)
129 self.assertEqual(oldlen+1, newlen)
130 self.assertEqual(ip.user_ns['In'][-1],'1;')
130 self.assertEqual(ip.user_ns['In'][-1],'1;')
131
131
132 def test_magic_names_in_string(self):
132 def test_magic_names_in_string(self):
133 ip.run_cell('a = """\n%exit\n"""')
133 ip.run_cell('a = """\n%exit\n"""')
134 self.assertEqual(ip.user_ns['a'], '\n%exit\n')
134 self.assertEqual(ip.user_ns['a'], '\n%exit\n')
135
135
136 def test_trailing_newline(self):
136 def test_trailing_newline(self):
137 """test that running !(command) does not raise a SyntaxError"""
137 """test that running !(command) does not raise a SyntaxError"""
138 ip.run_cell('!(true)\n', False)
138 ip.run_cell('!(true)\n', False)
139 ip.run_cell('!(true)\n\n\n', False)
139 ip.run_cell('!(true)\n\n\n', False)
140
140
141 def test_gh_597(self):
141 def test_gh_597(self):
142 """Pretty-printing lists of objects with non-ascii reprs may cause
142 """Pretty-printing lists of objects with non-ascii reprs may cause
143 problems."""
143 problems."""
144 class Spam(object):
144 class Spam(object):
145 def __repr__(self):
145 def __repr__(self):
146 return "\xe9"*50
146 return "\xe9"*50
147 import IPython.core.formatters
147 import IPython.core.formatters
148 f = IPython.core.formatters.PlainTextFormatter()
148 f = IPython.core.formatters.PlainTextFormatter()
149 f([Spam(), Spam()])
149 f([Spam(), Spam()])
150
150
151 def test_future_flags(self):
151 def test_future_flags(self):
152 """Check that future flags are used for parsing code (gh-777)"""
152 """Check that future flags are used for parsing code (gh-777)"""
153 ip.run_cell('from __future__ import barry_as_FLUFL')
153 ip.run_cell('from __future__ import barry_as_FLUFL')
154 try:
154 try:
155 ip.run_cell('prfunc_return_val = 1 <> 2')
155 ip.run_cell('prfunc_return_val = 1 <> 2')
156 assert 'prfunc_return_val' in ip.user_ns
156 assert 'prfunc_return_val' in ip.user_ns
157 finally:
157 finally:
158 # Reset compiler flags so we don't mess up other tests.
158 # Reset compiler flags so we don't mess up other tests.
159 ip.compile.reset_compiler_flags()
159 ip.compile.reset_compiler_flags()
160
160
161 def test_can_pickle(self):
161 def test_can_pickle(self):
162 "Can we pickle objects defined interactively (GH-29)"
162 "Can we pickle objects defined interactively (GH-29)"
163 ip = get_ipython()
163 ip = get_ipython()
164 ip.reset()
164 ip.reset()
165 ip.run_cell(("class Mylist(list):\n"
165 ip.run_cell(("class Mylist(list):\n"
166 " def __init__(self,x=[]):\n"
166 " def __init__(self,x=[]):\n"
167 " list.__init__(self,x)"))
167 " list.__init__(self,x)"))
168 ip.run_cell("w=Mylist([1,2,3])")
168 ip.run_cell("w=Mylist([1,2,3])")
169
169
170 from pickle import dumps
170 from pickle import dumps
171
171
172 # We need to swap in our main module - this is only necessary
172 # We need to swap in our main module - this is only necessary
173 # inside the test framework, because IPython puts the interactive module
173 # inside the test framework, because IPython puts the interactive module
174 # in place (but the test framework undoes this).
174 # in place (but the test framework undoes this).
175 _main = sys.modules['__main__']
175 _main = sys.modules['__main__']
176 sys.modules['__main__'] = ip.user_module
176 sys.modules['__main__'] = ip.user_module
177 try:
177 try:
178 res = dumps(ip.user_ns["w"])
178 res = dumps(ip.user_ns["w"])
179 finally:
179 finally:
180 sys.modules['__main__'] = _main
180 sys.modules['__main__'] = _main
181 self.assertTrue(isinstance(res, bytes))
181 self.assertTrue(isinstance(res, bytes))
182
182
183 def test_global_ns(self):
183 def test_global_ns(self):
184 "Code in functions must be able to access variables outside them."
184 "Code in functions must be able to access variables outside them."
185 ip = get_ipython()
185 ip = get_ipython()
186 ip.run_cell("a = 10")
186 ip.run_cell("a = 10")
187 ip.run_cell(("def f(x):\n"
187 ip.run_cell(("def f(x):\n"
188 " return x + a"))
188 " return x + a"))
189 ip.run_cell("b = f(12)")
189 ip.run_cell("b = f(12)")
190 self.assertEqual(ip.user_ns["b"], 22)
190 self.assertEqual(ip.user_ns["b"], 22)
191
191
192 def test_bad_custom_tb(self):
192 def test_bad_custom_tb(self):
193 """Check that InteractiveShell is protected from bad custom exception handlers"""
193 """Check that InteractiveShell is protected from bad custom exception handlers"""
194 ip.set_custom_exc((IOError,), lambda etype,value,tb: 1/0)
194 ip.set_custom_exc((IOError,), lambda etype,value,tb: 1/0)
195 self.assertEqual(ip.custom_exceptions, (IOError,))
195 self.assertEqual(ip.custom_exceptions, (IOError,))
196 with tt.AssertPrints("Custom TB Handler failed", channel='stderr'):
196 with tt.AssertPrints("Custom TB Handler failed", channel='stderr'):
197 ip.run_cell(u'raise IOError("foo")')
197 ip.run_cell(u'raise IOError("foo")')
198 self.assertEqual(ip.custom_exceptions, ())
198 self.assertEqual(ip.custom_exceptions, ())
199
199
200 def test_bad_custom_tb_return(self):
200 def test_bad_custom_tb_return(self):
201 """Check that InteractiveShell is protected from bad return types in custom exception handlers"""
201 """Check that InteractiveShell is protected from bad return types in custom exception handlers"""
202 ip.set_custom_exc((NameError,),lambda etype,value,tb, tb_offset=None: 1)
202 ip.set_custom_exc((NameError,),lambda etype,value,tb, tb_offset=None: 1)
203 self.assertEqual(ip.custom_exceptions, (NameError,))
203 self.assertEqual(ip.custom_exceptions, (NameError,))
204 with tt.AssertPrints("Custom TB Handler failed", channel='stderr'):
204 with tt.AssertPrints("Custom TB Handler failed", channel='stderr'):
205 ip.run_cell(u'a=abracadabra')
205 ip.run_cell(u'a=abracadabra')
206 self.assertEqual(ip.custom_exceptions, ())
206 self.assertEqual(ip.custom_exceptions, ())
207
207
208 def test_drop_by_id(self):
208 def test_drop_by_id(self):
209 myvars = {"a":object(), "b":object(), "c": object()}
209 myvars = {"a":object(), "b":object(), "c": object()}
210 ip.push(myvars, interactive=False)
210 ip.push(myvars, interactive=False)
211 for name in myvars:
211 for name in myvars:
212 assert name in ip.user_ns, name
212 assert name in ip.user_ns, name
213 assert name in ip.user_ns_hidden, name
213 assert name in ip.user_ns_hidden, name
214 ip.user_ns['b'] = 12
214 ip.user_ns['b'] = 12
215 ip.drop_by_id(myvars)
215 ip.drop_by_id(myvars)
216 for name in ["a", "c"]:
216 for name in ["a", "c"]:
217 assert name not in ip.user_ns, name
217 assert name not in ip.user_ns, name
218 assert name not in ip.user_ns_hidden, name
218 assert name not in ip.user_ns_hidden, name
219 assert ip.user_ns['b'] == 12
219 assert ip.user_ns['b'] == 12
220 ip.reset()
220 ip.reset()
221
221
222 def test_var_expand(self):
222 def test_var_expand(self):
223 ip.user_ns['f'] = u'Ca\xf1o'
223 ip.user_ns['f'] = u'Ca\xf1o'
224 self.assertEqual(ip.var_expand(u'echo $f'), u'echo Ca\xf1o')
224 self.assertEqual(ip.var_expand(u'echo $f'), u'echo Ca\xf1o')
225 self.assertEqual(ip.var_expand(u'echo {f}'), u'echo Ca\xf1o')
225 self.assertEqual(ip.var_expand(u'echo {f}'), u'echo Ca\xf1o')
226 self.assertEqual(ip.var_expand(u'echo {f[:-1]}'), u'echo Ca\xf1')
226 self.assertEqual(ip.var_expand(u'echo {f[:-1]}'), u'echo Ca\xf1')
227 self.assertEqual(ip.var_expand(u'echo {1*2}'), u'echo 2')
227 self.assertEqual(ip.var_expand(u'echo {1*2}'), u'echo 2')
228
228
229 self.assertEqual(ip.var_expand(u"grep x | awk '{print $1}'"), u"grep x | awk '{print $1}'")
229 self.assertEqual(ip.var_expand(u"grep x | awk '{print $1}'"), u"grep x | awk '{print $1}'")
230
230
231 ip.user_ns['f'] = b'Ca\xc3\xb1o'
231 ip.user_ns['f'] = b'Ca\xc3\xb1o'
232 # This should not raise any exception:
232 # This should not raise any exception:
233 ip.var_expand(u'echo $f')
233 ip.var_expand(u'echo $f')
234
234
235 def test_var_expand_local(self):
235 def test_var_expand_local(self):
236 """Test local variable expansion in !system and %magic calls"""
236 """Test local variable expansion in !system and %magic calls"""
237 # !system
237 # !system
238 ip.run_cell(
238 ip.run_cell(
239 "def test():\n"
239 "def test():\n"
240 ' lvar = "ttt"\n'
240 ' lvar = "ttt"\n'
241 " ret = !echo {lvar}\n"
241 " ret = !echo {lvar}\n"
242 " return ret[0]\n"
242 " return ret[0]\n"
243 )
243 )
244 res = ip.user_ns["test"]()
244 res = ip.user_ns["test"]()
245 self.assertIn("ttt", res)
245 self.assertIn("ttt", res)
246
246
247 # %magic
247 # %magic
248 ip.run_cell(
248 ip.run_cell(
249 "def makemacro():\n"
249 "def makemacro():\n"
250 ' macroname = "macro_var_expand_locals"\n'
250 ' macroname = "macro_var_expand_locals"\n'
251 " %macro {macroname} codestr\n"
251 " %macro {macroname} codestr\n"
252 )
252 )
253 ip.user_ns["codestr"] = "str(12)"
253 ip.user_ns["codestr"] = "str(12)"
254 ip.run_cell("makemacro()")
254 ip.run_cell("makemacro()")
255 self.assertIn("macro_var_expand_locals", ip.user_ns)
255 self.assertIn("macro_var_expand_locals", ip.user_ns)
256
256
257 def test_var_expand_self(self):
257 def test_var_expand_self(self):
258 """Test variable expansion with the name 'self', which was failing.
258 """Test variable expansion with the name 'self', which was failing.
259
259
260 See https://github.com/ipython/ipython/issues/1878#issuecomment-7698218
260 See https://github.com/ipython/ipython/issues/1878#issuecomment-7698218
261 """
261 """
262 ip.run_cell(
262 ip.run_cell(
263 "class cTest:\n"
263 "class cTest:\n"
264 ' classvar="see me"\n'
264 ' classvar="see me"\n'
265 " def test(self):\n"
265 " def test(self):\n"
266 " res = !echo Variable: {self.classvar}\n"
266 " res = !echo Variable: {self.classvar}\n"
267 " return res[0]\n"
267 " return res[0]\n"
268 )
268 )
269 self.assertIn("see me", ip.user_ns["cTest"]().test())
269 self.assertIn("see me", ip.user_ns["cTest"]().test())
270
270
271 def test_bad_var_expand(self):
271 def test_bad_var_expand(self):
272 """var_expand on invalid formats shouldn't raise"""
272 """var_expand on invalid formats shouldn't raise"""
273 # SyntaxError
273 # SyntaxError
274 self.assertEqual(ip.var_expand(u"{'a':5}"), u"{'a':5}")
274 self.assertEqual(ip.var_expand(u"{'a':5}"), u"{'a':5}")
275 # NameError
275 # NameError
276 self.assertEqual(ip.var_expand(u"{asdf}"), u"{asdf}")
276 self.assertEqual(ip.var_expand(u"{asdf}"), u"{asdf}")
277 # ZeroDivisionError
277 # ZeroDivisionError
278 self.assertEqual(ip.var_expand(u"{1/0}"), u"{1/0}")
278 self.assertEqual(ip.var_expand(u"{1/0}"), u"{1/0}")
279
279
280 def test_silent_postexec(self):
280 def test_silent_postexec(self):
281 """run_cell(silent=True) doesn't invoke pre/post_run_cell callbacks"""
281 """run_cell(silent=True) doesn't invoke pre/post_run_cell callbacks"""
282 pre_explicit = mock.Mock()
282 pre_explicit = mock.Mock()
283 pre_always = mock.Mock()
283 pre_always = mock.Mock()
284 post_explicit = mock.Mock()
284 post_explicit = mock.Mock()
285 post_always = mock.Mock()
285 post_always = mock.Mock()
286 all_mocks = [pre_explicit, pre_always, post_explicit, post_always]
286 all_mocks = [pre_explicit, pre_always, post_explicit, post_always]
287
287
288 ip.events.register('pre_run_cell', pre_explicit)
288 ip.events.register('pre_run_cell', pre_explicit)
289 ip.events.register('pre_execute', pre_always)
289 ip.events.register('pre_execute', pre_always)
290 ip.events.register('post_run_cell', post_explicit)
290 ip.events.register('post_run_cell', post_explicit)
291 ip.events.register('post_execute', post_always)
291 ip.events.register('post_execute', post_always)
292
292
293 try:
293 try:
294 ip.run_cell("1", silent=True)
294 ip.run_cell("1", silent=True)
295 assert pre_always.called
295 assert pre_always.called
296 assert not pre_explicit.called
296 assert not pre_explicit.called
297 assert post_always.called
297 assert post_always.called
298 assert not post_explicit.called
298 assert not post_explicit.called
299 # double-check that non-silent exec did what we expected
299 # double-check that non-silent exec did what we expected
300 # silent to avoid
300 # silent to avoid
301 ip.run_cell("1")
301 ip.run_cell("1")
302 assert pre_explicit.called
302 assert pre_explicit.called
303 assert post_explicit.called
303 assert post_explicit.called
304 info, = pre_explicit.call_args[0]
304 info, = pre_explicit.call_args[0]
305 result, = post_explicit.call_args[0]
305 result, = post_explicit.call_args[0]
306 self.assertEqual(info, result.info)
306 self.assertEqual(info, result.info)
307 # check that post hooks are always called
307 # check that post hooks are always called
308 [m.reset_mock() for m in all_mocks]
308 [m.reset_mock() for m in all_mocks]
309 ip.run_cell("syntax error")
309 ip.run_cell("syntax error")
310 assert pre_always.called
310 assert pre_always.called
311 assert pre_explicit.called
311 assert pre_explicit.called
312 assert post_always.called
312 assert post_always.called
313 assert post_explicit.called
313 assert post_explicit.called
314 info, = pre_explicit.call_args[0]
314 info, = pre_explicit.call_args[0]
315 result, = post_explicit.call_args[0]
315 result, = post_explicit.call_args[0]
316 self.assertEqual(info, result.info)
316 self.assertEqual(info, result.info)
317 finally:
317 finally:
318 # remove post-exec
318 # remove post-exec
319 ip.events.unregister('pre_run_cell', pre_explicit)
319 ip.events.unregister('pre_run_cell', pre_explicit)
320 ip.events.unregister('pre_execute', pre_always)
320 ip.events.unregister('pre_execute', pre_always)
321 ip.events.unregister('post_run_cell', post_explicit)
321 ip.events.unregister('post_run_cell', post_explicit)
322 ip.events.unregister('post_execute', post_always)
322 ip.events.unregister('post_execute', post_always)
323
323
324 def test_silent_noadvance(self):
324 def test_silent_noadvance(self):
325 """run_cell(silent=True) doesn't advance execution_count"""
325 """run_cell(silent=True) doesn't advance execution_count"""
326 ec = ip.execution_count
326 ec = ip.execution_count
327 # silent should force store_history=False
327 # silent should force store_history=False
328 ip.run_cell("1", store_history=True, silent=True)
328 ip.run_cell("1", store_history=True, silent=True)
329
329
330 self.assertEqual(ec, ip.execution_count)
330 self.assertEqual(ec, ip.execution_count)
331 # double-check that non-silent exec did what we expected
331 # double-check that non-silent exec did what we expected
332 # silent to avoid
332 # silent to avoid
333 ip.run_cell("1", store_history=True)
333 ip.run_cell("1", store_history=True)
334 self.assertEqual(ec+1, ip.execution_count)
334 self.assertEqual(ec+1, ip.execution_count)
335
335
336 def test_silent_nodisplayhook(self):
336 def test_silent_nodisplayhook(self):
337 """run_cell(silent=True) doesn't trigger displayhook"""
337 """run_cell(silent=True) doesn't trigger displayhook"""
338 d = dict(called=False)
338 d = dict(called=False)
339
339
340 trap = ip.display_trap
340 trap = ip.display_trap
341 save_hook = trap.hook
341 save_hook = trap.hook
342
342
343 def failing_hook(*args, **kwargs):
343 def failing_hook(*args, **kwargs):
344 d['called'] = True
344 d['called'] = True
345
345
346 try:
346 try:
347 trap.hook = failing_hook
347 trap.hook = failing_hook
348 res = ip.run_cell("1", silent=True)
348 res = ip.run_cell("1", silent=True)
349 self.assertFalse(d['called'])
349 self.assertFalse(d['called'])
350 self.assertIsNone(res.result)
350 self.assertIsNone(res.result)
351 # double-check that non-silent exec did what we expected
351 # double-check that non-silent exec did what we expected
352 # silent to avoid
352 # silent to avoid
353 ip.run_cell("1")
353 ip.run_cell("1")
354 self.assertTrue(d['called'])
354 self.assertTrue(d['called'])
355 finally:
355 finally:
356 trap.hook = save_hook
356 trap.hook = save_hook
357
357
358 def test_ofind_line_magic(self):
358 def test_ofind_line_magic(self):
359 from IPython.core.magic import register_line_magic
359 from IPython.core.magic import register_line_magic
360
360
361 @register_line_magic
361 @register_line_magic
362 def lmagic(line):
362 def lmagic(line):
363 "A line magic"
363 "A line magic"
364
364
365 # Get info on line magic
365 # Get info on line magic
366 lfind = ip._ofind("lmagic")
366 lfind = ip._ofind("lmagic")
367 info = OInfo(
367 info = OInfo(
368 found=True,
368 found=True,
369 isalias=False,
369 isalias=False,
370 ismagic=True,
370 ismagic=True,
371 namespace="IPython internal",
371 namespace="IPython internal",
372 obj=lmagic,
372 obj=lmagic,
373 parent=None,
373 parent=None,
374 )
374 )
375 self.assertEqual(lfind, info)
375 self.assertEqual(lfind, info)
376
376
377 def test_ofind_cell_magic(self):
377 def test_ofind_cell_magic(self):
378 from IPython.core.magic import register_cell_magic
378 from IPython.core.magic import register_cell_magic
379
379
380 @register_cell_magic
380 @register_cell_magic
381 def cmagic(line, cell):
381 def cmagic(line, cell):
382 "A cell magic"
382 "A cell magic"
383
383
384 # Get info on cell magic
384 # Get info on cell magic
385 find = ip._ofind("cmagic")
385 find = ip._ofind("cmagic")
386 info = OInfo(
386 info = OInfo(
387 found=True,
387 found=True,
388 isalias=False,
388 isalias=False,
389 ismagic=True,
389 ismagic=True,
390 namespace="IPython internal",
390 namespace="IPython internal",
391 obj=cmagic,
391 obj=cmagic,
392 parent=None,
392 parent=None,
393 )
393 )
394 self.assertEqual(find, info)
394 self.assertEqual(find, info)
395
395
396 def test_ofind_property_with_error(self):
396 def test_ofind_property_with_error(self):
397 class A(object):
397 class A(object):
398 @property
398 @property
399 def foo(self):
399 def foo(self):
400 raise NotImplementedError() # pragma: no cover
400 raise NotImplementedError() # pragma: no cover
401
401
402 a = A()
402 a = A()
403
403
404 found = ip._ofind("a.foo", [("locals", locals())])
404 found = ip._ofind("a.foo", [("locals", locals())])
405 info = OInfo(
405 info = OInfo(
406 found=True,
406 found=True,
407 isalias=False,
407 isalias=False,
408 ismagic=False,
408 ismagic=False,
409 namespace="locals",
409 namespace="locals",
410 obj=A.foo,
410 obj=A.foo,
411 parent=a,
411 parent=a,
412 )
412 )
413 self.assertEqual(found, info)
413 self.assertEqual(found, info)
414
414
415 def test_ofind_multiple_attribute_lookups(self):
415 def test_ofind_multiple_attribute_lookups(self):
416 class A(object):
416 class A(object):
417 @property
417 @property
418 def foo(self):
418 def foo(self):
419 raise NotImplementedError() # pragma: no cover
419 raise NotImplementedError() # pragma: no cover
420
420
421 a = A()
421 a = A()
422 a.a = A()
422 a.a = A()
423 a.a.a = A()
423 a.a.a = A()
424
424
425 found = ip._ofind("a.a.a.foo", [("locals", locals())])
425 found = ip._ofind("a.a.a.foo", [("locals", locals())])
426 info = OInfo(
426 info = OInfo(
427 found=True,
427 found=True,
428 isalias=False,
428 isalias=False,
429 ismagic=False,
429 ismagic=False,
430 namespace="locals",
430 namespace="locals",
431 obj=A.foo,
431 obj=A.foo,
432 parent=a.a.a,
432 parent=a.a.a,
433 )
433 )
434 self.assertEqual(found, info)
434 self.assertEqual(found, info)
435
435
436 def test_ofind_slotted_attributes(self):
436 def test_ofind_slotted_attributes(self):
437 class A(object):
437 class A(object):
438 __slots__ = ['foo']
438 __slots__ = ['foo']
439 def __init__(self):
439 def __init__(self):
440 self.foo = 'bar'
440 self.foo = 'bar'
441
441
442 a = A()
442 a = A()
443 found = ip._ofind("a.foo", [("locals", locals())])
443 found = ip._ofind("a.foo", [("locals", locals())])
444 info = OInfo(
444 info = OInfo(
445 found=True,
445 found=True,
446 isalias=False,
446 isalias=False,
447 ismagic=False,
447 ismagic=False,
448 namespace="locals",
448 namespace="locals",
449 obj=a.foo,
449 obj=a.foo,
450 parent=a,
450 parent=a,
451 )
451 )
452 self.assertEqual(found, info)
452 self.assertEqual(found, info)
453
453
454 found = ip._ofind("a.bar", [("locals", locals())])
454 found = ip._ofind("a.bar", [("locals", locals())])
455 expected = OInfo(
455 expected = OInfo(
456 found=False,
456 found=False,
457 isalias=False,
457 isalias=False,
458 ismagic=False,
458 ismagic=False,
459 namespace=None,
459 namespace=None,
460 obj=None,
460 obj=None,
461 parent=a,
461 parent=a,
462 )
462 )
463 assert found == expected
463 assert found == expected
464
464
465 def test_ofind_prefers_property_to_instance_level_attribute(self):
465 def test_ofind_prefers_property_to_instance_level_attribute(self):
466 class A(object):
466 class A(object):
467 @property
467 @property
468 def foo(self):
468 def foo(self):
469 return 'bar'
469 return 'bar'
470 a = A()
470 a = A()
471 a.__dict__["foo"] = "baz"
471 a.__dict__["foo"] = "baz"
472 self.assertEqual(a.foo, "bar")
472 self.assertEqual(a.foo, "bar")
473 found = ip._ofind("a.foo", [("locals", locals())])
473 found = ip._ofind("a.foo", [("locals", locals())])
474 self.assertIs(found.obj, A.foo)
474 self.assertIs(found.obj, A.foo)
475
475
476 def test_custom_syntaxerror_exception(self):
476 def test_custom_syntaxerror_exception(self):
477 called = []
477 called = []
478 def my_handler(shell, etype, value, tb, tb_offset=None):
478 def my_handler(shell, etype, value, tb, tb_offset=None):
479 called.append(etype)
479 called.append(etype)
480 shell.showtraceback((etype, value, tb), tb_offset=tb_offset)
480 shell.showtraceback((etype, value, tb), tb_offset=tb_offset)
481
481
482 ip.set_custom_exc((SyntaxError,), my_handler)
482 ip.set_custom_exc((SyntaxError,), my_handler)
483 try:
483 try:
484 ip.run_cell("1f")
484 ip.run_cell("1f")
485 # Check that this was called, and only once.
485 # Check that this was called, and only once.
486 self.assertEqual(called, [SyntaxError])
486 self.assertEqual(called, [SyntaxError])
487 finally:
487 finally:
488 # Reset the custom exception hook
488 # Reset the custom exception hook
489 ip.set_custom_exc((), None)
489 ip.set_custom_exc((), None)
490
490
491 def test_custom_exception(self):
491 def test_custom_exception(self):
492 called = []
492 called = []
493 def my_handler(shell, etype, value, tb, tb_offset=None):
493 def my_handler(shell, etype, value, tb, tb_offset=None):
494 called.append(etype)
494 called.append(etype)
495 shell.showtraceback((etype, value, tb), tb_offset=tb_offset)
495 shell.showtraceback((etype, value, tb), tb_offset=tb_offset)
496
496
497 ip.set_custom_exc((ValueError,), my_handler)
497 ip.set_custom_exc((ValueError,), my_handler)
498 try:
498 try:
499 res = ip.run_cell("raise ValueError('test')")
499 res = ip.run_cell("raise ValueError('test')")
500 # Check that this was called, and only once.
500 # Check that this was called, and only once.
501 self.assertEqual(called, [ValueError])
501 self.assertEqual(called, [ValueError])
502 # Check that the error is on the result object
502 # Check that the error is on the result object
503 self.assertIsInstance(res.error_in_exec, ValueError)
503 self.assertIsInstance(res.error_in_exec, ValueError)
504 finally:
504 finally:
505 # Reset the custom exception hook
505 # Reset the custom exception hook
506 ip.set_custom_exc((), None)
506 ip.set_custom_exc((), None)
507
507
508 @mock.patch("builtins.print")
508 @mock.patch("builtins.print")
509 def test_showtraceback_with_surrogates(self, mocked_print):
509 def test_showtraceback_with_surrogates(self, mocked_print):
510 values = []
510 values = []
511
511
512 def mock_print_func(value, sep=" ", end="\n", file=sys.stdout, flush=False):
512 def mock_print_func(value, sep=" ", end="\n", file=sys.stdout, flush=False):
513 values.append(value)
513 values.append(value)
514 if value == chr(0xD8FF):
514 if value == chr(0xD8FF):
515 raise UnicodeEncodeError("utf-8", chr(0xD8FF), 0, 1, "")
515 raise UnicodeEncodeError("utf-8", chr(0xD8FF), 0, 1, "")
516
516
517 # mock builtins.print
517 # mock builtins.print
518 mocked_print.side_effect = mock_print_func
518 mocked_print.side_effect = mock_print_func
519
519
520 # ip._showtraceback() is replaced in globalipapp.py.
520 # ip._showtraceback() is replaced in globalipapp.py.
521 # Call original method to test.
521 # Call original method to test.
522 interactiveshell.InteractiveShell._showtraceback(ip, None, None, chr(0xD8FF))
522 interactiveshell.InteractiveShell._showtraceback(ip, None, None, chr(0xD8FF))
523
523
524 self.assertEqual(mocked_print.call_count, 2)
524 self.assertEqual(mocked_print.call_count, 2)
525 self.assertEqual(values, [chr(0xD8FF), "\\ud8ff"])
525 self.assertEqual(values, [chr(0xD8FF), "\\ud8ff"])
526
526
527 def test_mktempfile(self):
527 def test_mktempfile(self):
528 filename = ip.mktempfile()
528 filename = ip.mktempfile()
529 # Check that we can open the file again on Windows
529 # Check that we can open the file again on Windows
530 with open(filename, "w", encoding="utf-8") as f:
530 with open(filename, "w", encoding="utf-8") as f:
531 f.write("abc")
531 f.write("abc")
532
532
533 filename = ip.mktempfile(data="blah")
533 filename = ip.mktempfile(data="blah")
534 with open(filename, "r", encoding="utf-8") as f:
534 with open(filename, "r", encoding="utf-8") as f:
535 self.assertEqual(f.read(), "blah")
535 self.assertEqual(f.read(), "blah")
536
536
537 def test_new_main_mod(self):
537 def test_new_main_mod(self):
538 # Smoketest to check that this accepts a unicode module name
538 # Smoketest to check that this accepts a unicode module name
539 name = u'jiefmw'
539 name = u'jiefmw'
540 mod = ip.new_main_mod(u'%s.py' % name, name)
540 mod = ip.new_main_mod(u'%s.py' % name, name)
541 self.assertEqual(mod.__name__, name)
541 self.assertEqual(mod.__name__, name)
542
542
543 def test_get_exception_only(self):
543 def test_get_exception_only(self):
544 try:
544 try:
545 raise KeyboardInterrupt
545 raise KeyboardInterrupt
546 except KeyboardInterrupt:
546 except KeyboardInterrupt:
547 msg = ip.get_exception_only()
547 msg = ip.get_exception_only()
548 self.assertEqual(msg, 'KeyboardInterrupt\n')
548 self.assertEqual(msg, 'KeyboardInterrupt\n')
549
549
550 try:
550 try:
551 raise DerivedInterrupt("foo")
551 raise DerivedInterrupt("foo")
552 except KeyboardInterrupt:
552 except KeyboardInterrupt:
553 msg = ip.get_exception_only()
553 msg = ip.get_exception_only()
554 self.assertEqual(msg, 'IPython.core.tests.test_interactiveshell.DerivedInterrupt: foo\n')
554 self.assertEqual(msg, 'IPython.core.tests.test_interactiveshell.DerivedInterrupt: foo\n')
555
555
556 def test_inspect_text(self):
556 def test_inspect_text(self):
557 ip.run_cell('a = 5')
557 ip.run_cell('a = 5')
558 text = ip.object_inspect_text('a')
558 text = ip.object_inspect_text('a')
559 self.assertIsInstance(text, str)
559 self.assertIsInstance(text, str)
560
560
561 def test_last_execution_result(self):
561 def test_last_execution_result(self):
562 """ Check that last execution result gets set correctly (GH-10702) """
562 """ Check that last execution result gets set correctly (GH-10702) """
563 result = ip.run_cell('a = 5; a')
563 result = ip.run_cell('a = 5; a')
564 self.assertTrue(ip.last_execution_succeeded)
564 self.assertTrue(ip.last_execution_succeeded)
565 self.assertEqual(ip.last_execution_result.result, 5)
565 self.assertEqual(ip.last_execution_result.result, 5)
566
566
567 result = ip.run_cell('a = x_invalid_id_x')
567 result = ip.run_cell('a = x_invalid_id_x')
568 self.assertFalse(ip.last_execution_succeeded)
568 self.assertFalse(ip.last_execution_succeeded)
569 self.assertFalse(ip.last_execution_result.success)
569 self.assertFalse(ip.last_execution_result.success)
570 self.assertIsInstance(ip.last_execution_result.error_in_exec, NameError)
570 self.assertIsInstance(ip.last_execution_result.error_in_exec, NameError)
571
571
572 def test_reset_aliasing(self):
572 def test_reset_aliasing(self):
573 """ Check that standard posix aliases work after %reset. """
573 """ Check that standard posix aliases work after %reset. """
574 if os.name != 'posix':
574 if os.name != 'posix':
575 return
575 return
576
576
577 ip.reset()
577 ip.reset()
578 for cmd in ('clear', 'more', 'less', 'man'):
578 for cmd in ('clear', 'more', 'less', 'man'):
579 res = ip.run_cell('%' + cmd)
579 res = ip.run_cell('%' + cmd)
580 self.assertEqual(res.success, True)
580 self.assertEqual(res.success, True)
581
581
582
582
583 @pytest.mark.skipif(
583 @pytest.mark.skipif(
584 sys.implementation.name == "pypy"
584 sys.implementation.name == "pypy"
585 and ((7, 3, 13) < sys.implementation.version < (7, 3, 16)),
585 and ((7, 3, 13) < sys.implementation.version < (7, 3, 16)),
586 reason="Unicode issues with scandir on PyPy, see https://github.com/pypy/pypy/issues/4860",
586 reason="Unicode issues with scandir on PyPy, see https://github.com/pypy/pypy/issues/4860",
587 )
587 )
588 class TestSafeExecfileNonAsciiPath(unittest.TestCase):
588 class TestSafeExecfileNonAsciiPath(unittest.TestCase):
589 @onlyif_unicode_paths
589 @onlyif_unicode_paths
590 def setUp(self):
590 def setUp(self):
591 self.BASETESTDIR = tempfile.mkdtemp()
591 self.BASETESTDIR = tempfile.mkdtemp()
592 self.TESTDIR = join(self.BASETESTDIR, u"Γ₯Àâ")
592 self.TESTDIR = join(self.BASETESTDIR, u"Γ₯Àâ")
593 os.mkdir(self.TESTDIR)
593 os.mkdir(self.TESTDIR)
594 with open(
594 with open(
595 join(self.TESTDIR, "Γ₯Àâtestscript.py"), "w", encoding="utf-8"
595 join(self.TESTDIR, "Γ₯Àâtestscript.py"), "w", encoding="utf-8"
596 ) as sfile:
596 ) as sfile:
597 sfile.write("pass\n")
597 sfile.write("pass\n")
598 self.oldpath = os.getcwd()
598 self.oldpath = os.getcwd()
599 os.chdir(self.TESTDIR)
599 os.chdir(self.TESTDIR)
600 self.fname = u"Γ₯Àâtestscript.py"
600 self.fname = u"Γ₯Àâtestscript.py"
601
601
602 def tearDown(self):
602 def tearDown(self):
603 os.chdir(self.oldpath)
603 os.chdir(self.oldpath)
604 shutil.rmtree(self.BASETESTDIR)
604 shutil.rmtree(self.BASETESTDIR)
605
605
606 @onlyif_unicode_paths
606 @onlyif_unicode_paths
607 def test_1(self):
607 def test_1(self):
608 """Test safe_execfile with non-ascii path
608 """Test safe_execfile with non-ascii path
609 """
609 """
610 ip.safe_execfile(self.fname, {}, raise_exceptions=True)
610 ip.safe_execfile(self.fname, {}, raise_exceptions=True)
611
611
612 class ExitCodeChecks(tt.TempFileMixin):
612 class ExitCodeChecks(tt.TempFileMixin):
613
613
614 def setUp(self):
614 def setUp(self):
615 self.system = ip.system_raw
615 self.system = ip.system_raw
616
616
617 def test_exit_code_ok(self):
617 def test_exit_code_ok(self):
618 self.system('exit 0')
618 self.system('exit 0')
619 self.assertEqual(ip.user_ns['_exit_code'], 0)
619 self.assertEqual(ip.user_ns['_exit_code'], 0)
620
620
621 def test_exit_code_error(self):
621 def test_exit_code_error(self):
622 self.system('exit 1')
622 self.system('exit 1')
623 self.assertEqual(ip.user_ns['_exit_code'], 1)
623 self.assertEqual(ip.user_ns['_exit_code'], 1)
624
624
625 @skipif(not hasattr(signal, 'SIGALRM'))
625 @skipif(not hasattr(signal, 'SIGALRM'))
626 def test_exit_code_signal(self):
626 def test_exit_code_signal(self):
627 self.mktmp("import signal, time\n"
627 self.mktmp("import signal, time\n"
628 "signal.setitimer(signal.ITIMER_REAL, 0.1)\n"
628 "signal.setitimer(signal.ITIMER_REAL, 0.1)\n"
629 "time.sleep(1)\n")
629 "time.sleep(1)\n")
630 self.system("%s %s" % (sys.executable, self.fname))
630 self.system("%s %s" % (sys.executable, self.fname))
631 self.assertEqual(ip.user_ns['_exit_code'], -signal.SIGALRM)
631 self.assertEqual(ip.user_ns['_exit_code'], -signal.SIGALRM)
632
632
633 @onlyif_cmds_exist("csh")
633 @onlyif_cmds_exist("csh")
634 def test_exit_code_signal_csh(self): # pragma: no cover
634 def test_exit_code_signal_csh(self): # pragma: no cover
635 SHELL = os.environ.get("SHELL", None)
635 SHELL = os.environ.get("SHELL", None)
636 os.environ["SHELL"] = find_cmd("csh")
636 os.environ["SHELL"] = find_cmd("csh")
637 try:
637 try:
638 self.test_exit_code_signal()
638 self.test_exit_code_signal()
639 finally:
639 finally:
640 if SHELL is not None:
640 if SHELL is not None:
641 os.environ['SHELL'] = SHELL
641 os.environ['SHELL'] = SHELL
642 else:
642 else:
643 del os.environ['SHELL']
643 del os.environ['SHELL']
644
644
645
645
646 class TestSystemRaw(ExitCodeChecks):
646 class TestSystemRaw(ExitCodeChecks):
647
647
648 def setUp(self):
648 def setUp(self):
649 super().setUp()
649 super().setUp()
650 self.system = ip.system_raw
650 self.system = ip.system_raw
651
651
652 @onlyif_unicode_paths
652 @onlyif_unicode_paths
653 def test_1(self):
653 def test_1(self):
654 """Test system_raw with non-ascii cmd
654 """Test system_raw with non-ascii cmd
655 """
655 """
656 cmd = u'''python -c "'Γ₯Àâ'" '''
656 cmd = u'''python -c "'Γ₯Àâ'" '''
657 ip.system_raw(cmd)
657 ip.system_raw(cmd)
658
658
659 @mock.patch('subprocess.call', side_effect=KeyboardInterrupt)
659 @mock.patch('subprocess.call', side_effect=KeyboardInterrupt)
660 @mock.patch('os.system', side_effect=KeyboardInterrupt)
660 @mock.patch('os.system', side_effect=KeyboardInterrupt)
661 def test_control_c(self, *mocks):
661 def test_control_c(self, *mocks):
662 try:
662 try:
663 self.system("sleep 1 # wont happen")
663 self.system("sleep 1 # won't happen")
664 except KeyboardInterrupt: # pragma: no cove
664 except KeyboardInterrupt: # pragma: no cove
665 self.fail(
665 self.fail(
666 "system call should intercept "
666 "system call should intercept "
667 "keyboard interrupt from subprocess.call"
667 "keyboard interrupt from subprocess.call"
668 )
668 )
669 self.assertEqual(ip.user_ns["_exit_code"], -signal.SIGINT)
669 self.assertEqual(ip.user_ns["_exit_code"], -signal.SIGINT)
670
670
671
671
672 @pytest.mark.parametrize("magic_cmd", ["pip", "conda", "cd"])
672 @pytest.mark.parametrize("magic_cmd", ["pip", "conda", "cd"])
673 def test_magic_warnings(magic_cmd):
673 def test_magic_warnings(magic_cmd):
674 if sys.platform == "win32":
674 if sys.platform == "win32":
675 to_mock = "os.system"
675 to_mock = "os.system"
676 expected_arg, expected_kwargs = magic_cmd, dict()
676 expected_arg, expected_kwargs = magic_cmd, dict()
677 else:
677 else:
678 to_mock = "subprocess.call"
678 to_mock = "subprocess.call"
679 expected_arg, expected_kwargs = magic_cmd, dict(
679 expected_arg, expected_kwargs = magic_cmd, dict(
680 shell=True, executable=os.environ.get("SHELL", None)
680 shell=True, executable=os.environ.get("SHELL", None)
681 )
681 )
682
682
683 with mock.patch(to_mock, return_value=0) as mock_sub:
683 with mock.patch(to_mock, return_value=0) as mock_sub:
684 with pytest.warns(Warning, match=r"You executed the system command"):
684 with pytest.warns(Warning, match=r"You executed the system command"):
685 ip.system_raw(magic_cmd)
685 ip.system_raw(magic_cmd)
686 mock_sub.assert_called_once_with(expected_arg, **expected_kwargs)
686 mock_sub.assert_called_once_with(expected_arg, **expected_kwargs)
687
687
688
688
689 # TODO: Exit codes are currently ignored on Windows.
689 # TODO: Exit codes are currently ignored on Windows.
690 class TestSystemPipedExitCode(ExitCodeChecks):
690 class TestSystemPipedExitCode(ExitCodeChecks):
691
691
692 def setUp(self):
692 def setUp(self):
693 super().setUp()
693 super().setUp()
694 self.system = ip.system_piped
694 self.system = ip.system_piped
695
695
696 @skip_win32
696 @skip_win32
697 def test_exit_code_ok(self):
697 def test_exit_code_ok(self):
698 ExitCodeChecks.test_exit_code_ok(self)
698 ExitCodeChecks.test_exit_code_ok(self)
699
699
700 @skip_win32
700 @skip_win32
701 def test_exit_code_error(self):
701 def test_exit_code_error(self):
702 ExitCodeChecks.test_exit_code_error(self)
702 ExitCodeChecks.test_exit_code_error(self)
703
703
704 @skip_win32
704 @skip_win32
705 def test_exit_code_signal(self):
705 def test_exit_code_signal(self):
706 ExitCodeChecks.test_exit_code_signal(self)
706 ExitCodeChecks.test_exit_code_signal(self)
707
707
708 class TestModules(tt.TempFileMixin):
708 class TestModules(tt.TempFileMixin):
709 def test_extraneous_loads(self):
709 def test_extraneous_loads(self):
710 """Test we're not loading modules on startup that we shouldn't.
710 """Test we're not loading modules on startup that we shouldn't.
711 """
711 """
712 self.mktmp("import sys\n"
712 self.mktmp("import sys\n"
713 "print('numpy' in sys.modules)\n"
713 "print('numpy' in sys.modules)\n"
714 "print('ipyparallel' in sys.modules)\n"
714 "print('ipyparallel' in sys.modules)\n"
715 "print('ipykernel' in sys.modules)\n"
715 "print('ipykernel' in sys.modules)\n"
716 )
716 )
717 out = "False\nFalse\nFalse\n"
717 out = "False\nFalse\nFalse\n"
718 tt.ipexec_validate(self.fname, out)
718 tt.ipexec_validate(self.fname, out)
719
719
720 class Negator(ast.NodeTransformer):
720 class Negator(ast.NodeTransformer):
721 """Negates all number literals in an AST."""
721 """Negates all number literals in an AST."""
722
722
723 def visit_Num(self, node):
723 def visit_Num(self, node):
724 node.n = -node.n
724 node.n = -node.n
725 return node
725 return node
726
726
727 def visit_Constant(self, node):
727 def visit_Constant(self, node):
728 if isinstance(node.value, int):
728 if isinstance(node.value, int):
729 return self.visit_Num(node)
729 return self.visit_Num(node)
730 return node
730 return node
731
731
732 class TestAstTransform(unittest.TestCase):
732 class TestAstTransform(unittest.TestCase):
733 def setUp(self):
733 def setUp(self):
734 self.negator = Negator()
734 self.negator = Negator()
735 ip.ast_transformers.append(self.negator)
735 ip.ast_transformers.append(self.negator)
736
736
737 def tearDown(self):
737 def tearDown(self):
738 ip.ast_transformers.remove(self.negator)
738 ip.ast_transformers.remove(self.negator)
739
739
740 def test_non_int_const(self):
740 def test_non_int_const(self):
741 with tt.AssertPrints("hello"):
741 with tt.AssertPrints("hello"):
742 ip.run_cell('print("hello")')
742 ip.run_cell('print("hello")')
743
743
744 def test_run_cell(self):
744 def test_run_cell(self):
745 with tt.AssertPrints("-34"):
745 with tt.AssertPrints("-34"):
746 ip.run_cell("print(12 + 22)")
746 ip.run_cell("print(12 + 22)")
747
747
748 # A named reference to a number shouldn't be transformed.
748 # A named reference to a number shouldn't be transformed.
749 ip.user_ns["n"] = 55
749 ip.user_ns["n"] = 55
750 with tt.AssertNotPrints("-55"):
750 with tt.AssertNotPrints("-55"):
751 ip.run_cell("print(n)")
751 ip.run_cell("print(n)")
752
752
753 def test_timeit(self):
753 def test_timeit(self):
754 called = set()
754 called = set()
755 def f(x):
755 def f(x):
756 called.add(x)
756 called.add(x)
757 ip.push({'f':f})
757 ip.push({'f':f})
758
758
759 with tt.AssertPrints("std. dev. of"):
759 with tt.AssertPrints("std. dev. of"):
760 ip.run_line_magic("timeit", "-n1 f(1)")
760 ip.run_line_magic("timeit", "-n1 f(1)")
761 self.assertEqual(called, {-1})
761 self.assertEqual(called, {-1})
762 called.clear()
762 called.clear()
763
763
764 with tt.AssertPrints("std. dev. of"):
764 with tt.AssertPrints("std. dev. of"):
765 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
765 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
766 self.assertEqual(called, {-2, -3})
766 self.assertEqual(called, {-2, -3})
767
767
768 def test_time(self):
768 def test_time(self):
769 called = []
769 called = []
770 def f(x):
770 def f(x):
771 called.append(x)
771 called.append(x)
772 ip.push({'f':f})
772 ip.push({'f':f})
773
773
774 # Test with an expression
774 # Test with an expression
775 with tt.AssertPrints("Wall time: "):
775 with tt.AssertPrints("Wall time: "):
776 ip.run_line_magic("time", "f(5+9)")
776 ip.run_line_magic("time", "f(5+9)")
777 self.assertEqual(called, [-14])
777 self.assertEqual(called, [-14])
778 called[:] = []
778 called[:] = []
779
779
780 # Test with a statement (different code path)
780 # Test with a statement (different code path)
781 with tt.AssertPrints("Wall time: "):
781 with tt.AssertPrints("Wall time: "):
782 ip.run_line_magic("time", "a = f(-3 + -2)")
782 ip.run_line_magic("time", "a = f(-3 + -2)")
783 self.assertEqual(called, [5])
783 self.assertEqual(called, [5])
784
784
785 def test_macro(self):
785 def test_macro(self):
786 ip.push({'a':10})
786 ip.push({'a':10})
787 # The AST transformation makes this do a+=-1
787 # The AST transformation makes this do a+=-1
788 ip.define_macro("amacro", "a+=1\nprint(a)")
788 ip.define_macro("amacro", "a+=1\nprint(a)")
789
789
790 with tt.AssertPrints("9"):
790 with tt.AssertPrints("9"):
791 ip.run_cell("amacro")
791 ip.run_cell("amacro")
792 with tt.AssertPrints("8"):
792 with tt.AssertPrints("8"):
793 ip.run_cell("amacro")
793 ip.run_cell("amacro")
794
794
795 class TestMiscTransform(unittest.TestCase):
795 class TestMiscTransform(unittest.TestCase):
796
796
797
797
798 def test_transform_only_once(self):
798 def test_transform_only_once(self):
799 cleanup = 0
799 cleanup = 0
800 line_t = 0
800 line_t = 0
801 def count_cleanup(lines):
801 def count_cleanup(lines):
802 nonlocal cleanup
802 nonlocal cleanup
803 cleanup += 1
803 cleanup += 1
804 return lines
804 return lines
805
805
806 def count_line_t(lines):
806 def count_line_t(lines):
807 nonlocal line_t
807 nonlocal line_t
808 line_t += 1
808 line_t += 1
809 return lines
809 return lines
810
810
811 ip.input_transformer_manager.cleanup_transforms.append(count_cleanup)
811 ip.input_transformer_manager.cleanup_transforms.append(count_cleanup)
812 ip.input_transformer_manager.line_transforms.append(count_line_t)
812 ip.input_transformer_manager.line_transforms.append(count_line_t)
813
813
814 ip.run_cell('1')
814 ip.run_cell('1')
815
815
816 assert cleanup == 1
816 assert cleanup == 1
817 assert line_t == 1
817 assert line_t == 1
818
818
819 class IntegerWrapper(ast.NodeTransformer):
819 class IntegerWrapper(ast.NodeTransformer):
820 """Wraps all integers in a call to Integer()"""
820 """Wraps all integers in a call to Integer()"""
821
821
822 # for Python 3.7 and earlier
822 # for Python 3.7 and earlier
823
823
824 # for Python 3.7 and earlier
824 # for Python 3.7 and earlier
825 def visit_Num(self, node):
825 def visit_Num(self, node):
826 if isinstance(node.n, int):
826 if isinstance(node.n, int):
827 return ast.Call(func=ast.Name(id='Integer', ctx=ast.Load()),
827 return ast.Call(func=ast.Name(id='Integer', ctx=ast.Load()),
828 args=[node], keywords=[])
828 args=[node], keywords=[])
829 return node
829 return node
830
830
831 # For Python 3.8+
831 # For Python 3.8+
832 def visit_Constant(self, node):
832 def visit_Constant(self, node):
833 if isinstance(node.value, int):
833 if isinstance(node.value, int):
834 return self.visit_Num(node)
834 return self.visit_Num(node)
835 return node
835 return node
836
836
837
837
838 class TestAstTransform2(unittest.TestCase):
838 class TestAstTransform2(unittest.TestCase):
839 def setUp(self):
839 def setUp(self):
840 self.intwrapper = IntegerWrapper()
840 self.intwrapper = IntegerWrapper()
841 ip.ast_transformers.append(self.intwrapper)
841 ip.ast_transformers.append(self.intwrapper)
842
842
843 self.calls = []
843 self.calls = []
844 def Integer(*args):
844 def Integer(*args):
845 self.calls.append(args)
845 self.calls.append(args)
846 return args
846 return args
847 ip.push({"Integer": Integer})
847 ip.push({"Integer": Integer})
848
848
849 def tearDown(self):
849 def tearDown(self):
850 ip.ast_transformers.remove(self.intwrapper)
850 ip.ast_transformers.remove(self.intwrapper)
851 del ip.user_ns['Integer']
851 del ip.user_ns['Integer']
852
852
853 def test_run_cell(self):
853 def test_run_cell(self):
854 ip.run_cell("n = 2")
854 ip.run_cell("n = 2")
855 self.assertEqual(self.calls, [(2,)])
855 self.assertEqual(self.calls, [(2,)])
856
856
857 # This shouldn't throw an error
857 # This shouldn't throw an error
858 ip.run_cell("o = 2.0")
858 ip.run_cell("o = 2.0")
859 self.assertEqual(ip.user_ns['o'], 2.0)
859 self.assertEqual(ip.user_ns['o'], 2.0)
860
860
861 def test_run_cell_non_int(self):
861 def test_run_cell_non_int(self):
862 ip.run_cell("n = 'a'")
862 ip.run_cell("n = 'a'")
863 assert self.calls == []
863 assert self.calls == []
864
864
865 def test_timeit(self):
865 def test_timeit(self):
866 called = set()
866 called = set()
867 def f(x):
867 def f(x):
868 called.add(x)
868 called.add(x)
869 ip.push({'f':f})
869 ip.push({'f':f})
870
870
871 with tt.AssertPrints("std. dev. of"):
871 with tt.AssertPrints("std. dev. of"):
872 ip.run_line_magic("timeit", "-n1 f(1)")
872 ip.run_line_magic("timeit", "-n1 f(1)")
873 self.assertEqual(called, {(1,)})
873 self.assertEqual(called, {(1,)})
874 called.clear()
874 called.clear()
875
875
876 with tt.AssertPrints("std. dev. of"):
876 with tt.AssertPrints("std. dev. of"):
877 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
877 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
878 self.assertEqual(called, {(2,), (3,)})
878 self.assertEqual(called, {(2,), (3,)})
879
879
880 class ErrorTransformer(ast.NodeTransformer):
880 class ErrorTransformer(ast.NodeTransformer):
881 """Throws an error when it sees a number."""
881 """Throws an error when it sees a number."""
882
882
883 def visit_Constant(self, node):
883 def visit_Constant(self, node):
884 if isinstance(node.value, int):
884 if isinstance(node.value, int):
885 raise ValueError("test")
885 raise ValueError("test")
886 return node
886 return node
887
887
888
888
889 class TestAstTransformError(unittest.TestCase):
889 class TestAstTransformError(unittest.TestCase):
890 def test_unregistering(self):
890 def test_unregistering(self):
891 err_transformer = ErrorTransformer()
891 err_transformer = ErrorTransformer()
892 ip.ast_transformers.append(err_transformer)
892 ip.ast_transformers.append(err_transformer)
893
893
894 with self.assertWarnsRegex(UserWarning, "It will be unregistered"):
894 with self.assertWarnsRegex(UserWarning, "It will be unregistered"):
895 ip.run_cell("1 + 2")
895 ip.run_cell("1 + 2")
896
896
897 # This should have been removed.
897 # This should have been removed.
898 self.assertNotIn(err_transformer, ip.ast_transformers)
898 self.assertNotIn(err_transformer, ip.ast_transformers)
899
899
900
900
901 class StringRejector(ast.NodeTransformer):
901 class StringRejector(ast.NodeTransformer):
902 """Throws an InputRejected when it sees a string literal.
902 """Throws an InputRejected when it sees a string literal.
903
903
904 Used to verify that NodeTransformers can signal that a piece of code should
904 Used to verify that NodeTransformers can signal that a piece of code should
905 not be executed by throwing an InputRejected.
905 not be executed by throwing an InputRejected.
906 """
906 """
907
907
908 def visit_Constant(self, node):
908 def visit_Constant(self, node):
909 if isinstance(node.value, str):
909 if isinstance(node.value, str):
910 raise InputRejected("test")
910 raise InputRejected("test")
911 return node
911 return node
912
912
913
913
914 class TestAstTransformInputRejection(unittest.TestCase):
914 class TestAstTransformInputRejection(unittest.TestCase):
915
915
916 def setUp(self):
916 def setUp(self):
917 self.transformer = StringRejector()
917 self.transformer = StringRejector()
918 ip.ast_transformers.append(self.transformer)
918 ip.ast_transformers.append(self.transformer)
919
919
920 def tearDown(self):
920 def tearDown(self):
921 ip.ast_transformers.remove(self.transformer)
921 ip.ast_transformers.remove(self.transformer)
922
922
923 def test_input_rejection(self):
923 def test_input_rejection(self):
924 """Check that NodeTransformers can reject input."""
924 """Check that NodeTransformers can reject input."""
925
925
926 expect_exception_tb = tt.AssertPrints("InputRejected: test")
926 expect_exception_tb = tt.AssertPrints("InputRejected: test")
927 expect_no_cell_output = tt.AssertNotPrints("'unsafe'", suppress=False)
927 expect_no_cell_output = tt.AssertNotPrints("'unsafe'", suppress=False)
928
928
929 # Run the same check twice to verify that the transformer is not
929 # Run the same check twice to verify that the transformer is not
930 # disabled after raising.
930 # disabled after raising.
931 with expect_exception_tb, expect_no_cell_output:
931 with expect_exception_tb, expect_no_cell_output:
932 ip.run_cell("'unsafe'")
932 ip.run_cell("'unsafe'")
933
933
934 with expect_exception_tb, expect_no_cell_output:
934 with expect_exception_tb, expect_no_cell_output:
935 res = ip.run_cell("'unsafe'")
935 res = ip.run_cell("'unsafe'")
936
936
937 self.assertIsInstance(res.error_before_exec, InputRejected)
937 self.assertIsInstance(res.error_before_exec, InputRejected)
938
938
939 def test__IPYTHON__():
939 def test__IPYTHON__():
940 # This shouldn't raise a NameError, that's all
940 # This shouldn't raise a NameError, that's all
941 __IPYTHON__
941 __IPYTHON__
942
942
943
943
944 class DummyRepr(object):
944 class DummyRepr(object):
945 def __repr__(self):
945 def __repr__(self):
946 return "DummyRepr"
946 return "DummyRepr"
947
947
948 def _repr_html_(self):
948 def _repr_html_(self):
949 return "<b>dummy</b>"
949 return "<b>dummy</b>"
950
950
951 def _repr_javascript_(self):
951 def _repr_javascript_(self):
952 return "console.log('hi');", {'key': 'value'}
952 return "console.log('hi');", {'key': 'value'}
953
953
954
954
955 def test_user_variables():
955 def test_user_variables():
956 # enable all formatters
956 # enable all formatters
957 ip.display_formatter.active_types = ip.display_formatter.format_types
957 ip.display_formatter.active_types = ip.display_formatter.format_types
958
958
959 ip.user_ns['dummy'] = d = DummyRepr()
959 ip.user_ns['dummy'] = d = DummyRepr()
960 keys = {'dummy', 'doesnotexist'}
960 keys = {'dummy', 'doesnotexist'}
961 r = ip.user_expressions({ key:key for key in keys})
961 r = ip.user_expressions({ key:key for key in keys})
962
962
963 assert keys == set(r.keys())
963 assert keys == set(r.keys())
964 dummy = r["dummy"]
964 dummy = r["dummy"]
965 assert {"status", "data", "metadata"} == set(dummy.keys())
965 assert {"status", "data", "metadata"} == set(dummy.keys())
966 assert dummy["status"] == "ok"
966 assert dummy["status"] == "ok"
967 data = dummy["data"]
967 data = dummy["data"]
968 metadata = dummy["metadata"]
968 metadata = dummy["metadata"]
969 assert data.get("text/html") == d._repr_html_()
969 assert data.get("text/html") == d._repr_html_()
970 js, jsmd = d._repr_javascript_()
970 js, jsmd = d._repr_javascript_()
971 assert data.get("application/javascript") == js
971 assert data.get("application/javascript") == js
972 assert metadata.get("application/javascript") == jsmd
972 assert metadata.get("application/javascript") == jsmd
973
973
974 dne = r["doesnotexist"]
974 dne = r["doesnotexist"]
975 assert dne["status"] == "error"
975 assert dne["status"] == "error"
976 assert dne["ename"] == "NameError"
976 assert dne["ename"] == "NameError"
977
977
978 # back to text only
978 # back to text only
979 ip.display_formatter.active_types = ['text/plain']
979 ip.display_formatter.active_types = ['text/plain']
980
980
981 def test_user_expression():
981 def test_user_expression():
982 # enable all formatters
982 # enable all formatters
983 ip.display_formatter.active_types = ip.display_formatter.format_types
983 ip.display_formatter.active_types = ip.display_formatter.format_types
984 query = {
984 query = {
985 'a' : '1 + 2',
985 'a' : '1 + 2',
986 'b' : '1/0',
986 'b' : '1/0',
987 }
987 }
988 r = ip.user_expressions(query)
988 r = ip.user_expressions(query)
989 import pprint
989 import pprint
990 pprint.pprint(r)
990 pprint.pprint(r)
991 assert set(r.keys()) == set(query.keys())
991 assert set(r.keys()) == set(query.keys())
992 a = r["a"]
992 a = r["a"]
993 assert {"status", "data", "metadata"} == set(a.keys())
993 assert {"status", "data", "metadata"} == set(a.keys())
994 assert a["status"] == "ok"
994 assert a["status"] == "ok"
995 data = a["data"]
995 data = a["data"]
996 metadata = a["metadata"]
996 metadata = a["metadata"]
997 assert data.get("text/plain") == "3"
997 assert data.get("text/plain") == "3"
998
998
999 b = r["b"]
999 b = r["b"]
1000 assert b["status"] == "error"
1000 assert b["status"] == "error"
1001 assert b["ename"] == "ZeroDivisionError"
1001 assert b["ename"] == "ZeroDivisionError"
1002
1002
1003 # back to text only
1003 # back to text only
1004 ip.display_formatter.active_types = ['text/plain']
1004 ip.display_formatter.active_types = ['text/plain']
1005
1005
1006
1006
1007 class TestSyntaxErrorTransformer(unittest.TestCase):
1007 class TestSyntaxErrorTransformer(unittest.TestCase):
1008 """Check that SyntaxError raised by an input transformer is handled by run_cell()"""
1008 """Check that SyntaxError raised by an input transformer is handled by run_cell()"""
1009
1009
1010 @staticmethod
1010 @staticmethod
1011 def transformer(lines):
1011 def transformer(lines):
1012 for line in lines:
1012 for line in lines:
1013 pos = line.find('syntaxerror')
1013 pos = line.find('syntaxerror')
1014 if pos >= 0:
1014 if pos >= 0:
1015 e = SyntaxError('input contains "syntaxerror"')
1015 e = SyntaxError('input contains "syntaxerror"')
1016 e.text = line
1016 e.text = line
1017 e.offset = pos + 1
1017 e.offset = pos + 1
1018 raise e
1018 raise e
1019 return lines
1019 return lines
1020
1020
1021 def setUp(self):
1021 def setUp(self):
1022 ip.input_transformers_post.append(self.transformer)
1022 ip.input_transformers_post.append(self.transformer)
1023
1023
1024 def tearDown(self):
1024 def tearDown(self):
1025 ip.input_transformers_post.remove(self.transformer)
1025 ip.input_transformers_post.remove(self.transformer)
1026
1026
1027 def test_syntaxerror_input_transformer(self):
1027 def test_syntaxerror_input_transformer(self):
1028 with tt.AssertPrints('1234'):
1028 with tt.AssertPrints('1234'):
1029 ip.run_cell('1234')
1029 ip.run_cell('1234')
1030 with tt.AssertPrints('SyntaxError: invalid syntax'):
1030 with tt.AssertPrints('SyntaxError: invalid syntax'):
1031 ip.run_cell('1 2 3') # plain python syntax error
1031 ip.run_cell('1 2 3') # plain python syntax error
1032 with tt.AssertPrints('SyntaxError: input contains "syntaxerror"'):
1032 with tt.AssertPrints('SyntaxError: input contains "syntaxerror"'):
1033 ip.run_cell('2345 # syntaxerror') # input transformer syntax error
1033 ip.run_cell('2345 # syntaxerror') # input transformer syntax error
1034 with tt.AssertPrints('3456'):
1034 with tt.AssertPrints('3456'):
1035 ip.run_cell('3456')
1035 ip.run_cell('3456')
1036
1036
1037
1037
1038 class TestWarningSuppression(unittest.TestCase):
1038 class TestWarningSuppression(unittest.TestCase):
1039 def test_warning_suppression(self):
1039 def test_warning_suppression(self):
1040 ip.run_cell("import warnings")
1040 ip.run_cell("import warnings")
1041 try:
1041 try:
1042 with self.assertWarnsRegex(UserWarning, "asdf"):
1042 with self.assertWarnsRegex(UserWarning, "asdf"):
1043 ip.run_cell("warnings.warn('asdf')")
1043 ip.run_cell("warnings.warn('asdf')")
1044 # Here's the real test -- if we run that again, we should get the
1044 # Here's the real test -- if we run that again, we should get the
1045 # warning again. Traditionally, each warning was only issued once per
1045 # warning again. Traditionally, each warning was only issued once per
1046 # IPython session (approximately), even if the user typed in new and
1046 # IPython session (approximately), even if the user typed in new and
1047 # different code that should have also triggered the warning, leading
1047 # different code that should have also triggered the warning, leading
1048 # to much confusion.
1048 # to much confusion.
1049 with self.assertWarnsRegex(UserWarning, "asdf"):
1049 with self.assertWarnsRegex(UserWarning, "asdf"):
1050 ip.run_cell("warnings.warn('asdf')")
1050 ip.run_cell("warnings.warn('asdf')")
1051 finally:
1051 finally:
1052 ip.run_cell("del warnings")
1052 ip.run_cell("del warnings")
1053
1053
1054
1054
1055 def test_deprecation_warning(self):
1055 def test_deprecation_warning(self):
1056 ip.run_cell("""
1056 ip.run_cell("""
1057 import warnings
1057 import warnings
1058 def wrn():
1058 def wrn():
1059 warnings.warn(
1059 warnings.warn(
1060 "I AM A WARNING",
1060 "I AM A WARNING",
1061 DeprecationWarning
1061 DeprecationWarning
1062 )
1062 )
1063 """)
1063 """)
1064 try:
1064 try:
1065 with self.assertWarnsRegex(DeprecationWarning, "I AM A WARNING"):
1065 with self.assertWarnsRegex(DeprecationWarning, "I AM A WARNING"):
1066 ip.run_cell("wrn()")
1066 ip.run_cell("wrn()")
1067 finally:
1067 finally:
1068 ip.run_cell("del warnings")
1068 ip.run_cell("del warnings")
1069 ip.run_cell("del wrn")
1069 ip.run_cell("del wrn")
1070
1070
1071
1071
1072 class TestImportNoDeprecate(tt.TempFileMixin):
1072 class TestImportNoDeprecate(tt.TempFileMixin):
1073
1073
1074 def setUp(self):
1074 def setUp(self):
1075 """Make a valid python temp file."""
1075 """Make a valid python temp file."""
1076 self.mktmp("""
1076 self.mktmp("""
1077 import warnings
1077 import warnings
1078 def wrn():
1078 def wrn():
1079 warnings.warn(
1079 warnings.warn(
1080 "I AM A WARNING",
1080 "I AM A WARNING",
1081 DeprecationWarning
1081 DeprecationWarning
1082 )
1082 )
1083 """)
1083 """)
1084 super().setUp()
1084 super().setUp()
1085
1085
1086 def test_no_dep(self):
1086 def test_no_dep(self):
1087 """
1087 """
1088 No deprecation warning should be raised from imported functions
1088 No deprecation warning should be raised from imported functions
1089 """
1089 """
1090 ip.run_cell("from {} import wrn".format(self.fname))
1090 ip.run_cell("from {} import wrn".format(self.fname))
1091
1091
1092 with tt.AssertNotPrints("I AM A WARNING"):
1092 with tt.AssertNotPrints("I AM A WARNING"):
1093 ip.run_cell("wrn()")
1093 ip.run_cell("wrn()")
1094 ip.run_cell("del wrn")
1094 ip.run_cell("del wrn")
1095
1095
1096
1096
1097 def test_custom_exc_count():
1097 def test_custom_exc_count():
1098 hook = mock.Mock(return_value=None)
1098 hook = mock.Mock(return_value=None)
1099 ip.set_custom_exc((SyntaxError,), hook)
1099 ip.set_custom_exc((SyntaxError,), hook)
1100 before = ip.execution_count
1100 before = ip.execution_count
1101 ip.run_cell("def foo()", store_history=True)
1101 ip.run_cell("def foo()", store_history=True)
1102 # restore default excepthook
1102 # restore default excepthook
1103 ip.set_custom_exc((), None)
1103 ip.set_custom_exc((), None)
1104 assert hook.call_count == 1
1104 assert hook.call_count == 1
1105 assert ip.execution_count == before + 1
1105 assert ip.execution_count == before + 1
1106
1106
1107
1107
1108 def test_run_cell_async():
1108 def test_run_cell_async():
1109 ip.run_cell("import asyncio")
1109 ip.run_cell("import asyncio")
1110 coro = ip.run_cell_async("await asyncio.sleep(0.01)\n5")
1110 coro = ip.run_cell_async("await asyncio.sleep(0.01)\n5")
1111 assert asyncio.iscoroutine(coro)
1111 assert asyncio.iscoroutine(coro)
1112 loop = asyncio.new_event_loop()
1112 loop = asyncio.new_event_loop()
1113 result = loop.run_until_complete(coro)
1113 result = loop.run_until_complete(coro)
1114 assert isinstance(result, interactiveshell.ExecutionResult)
1114 assert isinstance(result, interactiveshell.ExecutionResult)
1115 assert result.result == 5
1115 assert result.result == 5
1116
1116
1117
1117
1118 def test_run_cell_await():
1118 def test_run_cell_await():
1119 ip.run_cell("import asyncio")
1119 ip.run_cell("import asyncio")
1120 result = ip.run_cell("await asyncio.sleep(0.01); 10")
1120 result = ip.run_cell("await asyncio.sleep(0.01); 10")
1121 assert ip.user_ns["_"] == 10
1121 assert ip.user_ns["_"] == 10
1122
1122
1123
1123
1124 def test_run_cell_asyncio_run():
1124 def test_run_cell_asyncio_run():
1125 ip.run_cell("import asyncio")
1125 ip.run_cell("import asyncio")
1126 result = ip.run_cell("await asyncio.sleep(0.01); 1")
1126 result = ip.run_cell("await asyncio.sleep(0.01); 1")
1127 assert ip.user_ns["_"] == 1
1127 assert ip.user_ns["_"] == 1
1128 result = ip.run_cell("asyncio.run(asyncio.sleep(0.01)); 2")
1128 result = ip.run_cell("asyncio.run(asyncio.sleep(0.01)); 2")
1129 assert ip.user_ns["_"] == 2
1129 assert ip.user_ns["_"] == 2
1130 result = ip.run_cell("await asyncio.sleep(0.01); 3")
1130 result = ip.run_cell("await asyncio.sleep(0.01); 3")
1131 assert ip.user_ns["_"] == 3
1131 assert ip.user_ns["_"] == 3
1132
1132
1133
1133
1134 def test_should_run_async():
1134 def test_should_run_async():
1135 assert not ip.should_run_async("a = 5", transformed_cell="a = 5")
1135 assert not ip.should_run_async("a = 5", transformed_cell="a = 5")
1136 assert ip.should_run_async("await x", transformed_cell="await x")
1136 assert ip.should_run_async("await x", transformed_cell="await x")
1137 assert ip.should_run_async(
1137 assert ip.should_run_async(
1138 "import asyncio; await asyncio.sleep(1)",
1138 "import asyncio; await asyncio.sleep(1)",
1139 transformed_cell="import asyncio; await asyncio.sleep(1)",
1139 transformed_cell="import asyncio; await asyncio.sleep(1)",
1140 )
1140 )
1141
1141
1142
1142
1143 def test_set_custom_completer():
1143 def test_set_custom_completer():
1144 num_completers = len(ip.Completer.matchers)
1144 num_completers = len(ip.Completer.matchers)
1145
1145
1146 def foo(*args, **kwargs):
1146 def foo(*args, **kwargs):
1147 return "I'm a completer!"
1147 return "I'm a completer!"
1148
1148
1149 ip.set_custom_completer(foo, 0)
1149 ip.set_custom_completer(foo, 0)
1150
1150
1151 # check that we've really added a new completer
1151 # check that we've really added a new completer
1152 assert len(ip.Completer.matchers) == num_completers + 1
1152 assert len(ip.Completer.matchers) == num_completers + 1
1153
1153
1154 # check that the first completer is the function we defined
1154 # check that the first completer is the function we defined
1155 assert ip.Completer.matchers[0]() == "I'm a completer!"
1155 assert ip.Completer.matchers[0]() == "I'm a completer!"
1156
1156
1157 # clean up
1157 # clean up
1158 ip.Completer.custom_matchers.pop()
1158 ip.Completer.custom_matchers.pop()
1159
1159
1160
1160
1161 class TestShowTracebackAttack(unittest.TestCase):
1161 class TestShowTracebackAttack(unittest.TestCase):
1162 """Test that the interactive shell is resilient against the client attack of
1162 """Test that the interactive shell is resilient against the client attack of
1163 manipulating the showtracebacks method. These attacks shouldn't result in an
1163 manipulating the showtracebacks method. These attacks shouldn't result in an
1164 unhandled exception in the kernel."""
1164 unhandled exception in the kernel."""
1165
1165
1166 def setUp(self):
1166 def setUp(self):
1167 self.orig_showtraceback = interactiveshell.InteractiveShell.showtraceback
1167 self.orig_showtraceback = interactiveshell.InteractiveShell.showtraceback
1168
1168
1169 def tearDown(self):
1169 def tearDown(self):
1170 interactiveshell.InteractiveShell.showtraceback = self.orig_showtraceback
1170 interactiveshell.InteractiveShell.showtraceback = self.orig_showtraceback
1171
1171
1172 def test_set_show_tracebacks_none(self):
1172 def test_set_show_tracebacks_none(self):
1173 """Test the case of the client setting showtracebacks to None"""
1173 """Test the case of the client setting showtracebacks to None"""
1174
1174
1175 result = ip.run_cell(
1175 result = ip.run_cell(
1176 """
1176 """
1177 import IPython.core.interactiveshell
1177 import IPython.core.interactiveshell
1178 IPython.core.interactiveshell.InteractiveShell.showtraceback = None
1178 IPython.core.interactiveshell.InteractiveShell.showtraceback = None
1179
1179
1180 assert False, "This should not raise an exception"
1180 assert False, "This should not raise an exception"
1181 """
1181 """
1182 )
1182 )
1183 print(result)
1183 print(result)
1184
1184
1185 assert result.result is None
1185 assert result.result is None
1186 assert isinstance(result.error_in_exec, TypeError)
1186 assert isinstance(result.error_in_exec, TypeError)
1187 assert str(result.error_in_exec) == "'NoneType' object is not callable"
1187 assert str(result.error_in_exec) == "'NoneType' object is not callable"
1188
1188
1189 def test_set_show_tracebacks_noop(self):
1189 def test_set_show_tracebacks_noop(self):
1190 """Test the case of the client setting showtracebacks to a no op lambda"""
1190 """Test the case of the client setting showtracebacks to a no op lambda"""
1191
1191
1192 result = ip.run_cell(
1192 result = ip.run_cell(
1193 """
1193 """
1194 import IPython.core.interactiveshell
1194 import IPython.core.interactiveshell
1195 IPython.core.interactiveshell.InteractiveShell.showtraceback = lambda *args, **kwargs: None
1195 IPython.core.interactiveshell.InteractiveShell.showtraceback = lambda *args, **kwargs: None
1196
1196
1197 assert False, "This should not raise an exception"
1197 assert False, "This should not raise an exception"
1198 """
1198 """
1199 )
1199 )
1200 print(result)
1200 print(result)
1201
1201
1202 assert result.result is None
1202 assert result.result is None
1203 assert isinstance(result.error_in_exec, AssertionError)
1203 assert isinstance(result.error_in_exec, AssertionError)
1204 assert str(result.error_in_exec) == "This should not raise an exception"
1204 assert str(result.error_in_exec) == "This should not raise an exception"
1205
1205
1206
1206
1207 @skip_if_not_osx
1207 @skip_if_not_osx
1208 def test_enable_gui_osx():
1208 def test_enable_gui_osx():
1209 simple_prompt = ip.simple_prompt
1209 simple_prompt = ip.simple_prompt
1210 ip.simple_prompt = False
1210 ip.simple_prompt = False
1211
1211
1212 ip.enable_gui("osx")
1212 ip.enable_gui("osx")
1213 assert ip.active_eventloop == "osx"
1213 assert ip.active_eventloop == "osx"
1214 ip.enable_gui()
1214 ip.enable_gui()
1215
1215
1216 # The following line fails for IPython <= 8.25.0
1216 # The following line fails for IPython <= 8.25.0
1217 ip.enable_gui("macosx")
1217 ip.enable_gui("macosx")
1218 assert ip.active_eventloop == "osx"
1218 assert ip.active_eventloop == "osx"
1219 ip.enable_gui()
1219 ip.enable_gui()
1220
1220
1221 ip.simple_prompt = simple_prompt
1221 ip.simple_prompt = simple_prompt
@@ -1,626 +1,626
1 # encoding: utf-8
1 # encoding: utf-8
2 """Tests for code execution (%run and related), which is particularly tricky.
2 """Tests for code execution (%run and related), which is particularly tricky.
3
3
4 Because of how %run manages namespaces, and the fact that we are trying here to
4 Because of how %run manages namespaces, and the fact that we are trying here to
5 verify subtle object deletion and reference counting issues, the %run tests
5 verify subtle object deletion and reference counting issues, the %run tests
6 will be kept in this separate file. This makes it easier to aggregate in one
6 will be kept in this separate file. This makes it easier to aggregate in one
7 place the tricks needed to handle it; most other magics are much easier to test
7 place the tricks needed to handle it; most other magics are much easier to test
8 and we do so in a common test_magic file.
8 and we do so in a common test_magic file.
9
9
10 Note that any test using `run -i` should make sure to do a `reset` afterwards,
10 Note that any test using `run -i` should make sure to do a `reset` afterwards,
11 as otherwise it may influence later tests.
11 as otherwise it may influence later tests.
12 """
12 """
13
13
14 # Copyright (c) IPython Development Team.
14 # Copyright (c) IPython Development Team.
15 # Distributed under the terms of the Modified BSD License.
15 # Distributed under the terms of the Modified BSD License.
16
16
17
17
18
18
19 import functools
19 import functools
20 import os
20 import os
21 import platform
21 import platform
22 import random
22 import random
23 import string
23 import string
24 import sys
24 import sys
25 import textwrap
25 import textwrap
26 import unittest
26 import unittest
27 from os.path import join as pjoin
27 from os.path import join as pjoin
28 from unittest.mock import patch
28 from unittest.mock import patch
29
29
30 import pytest
30 import pytest
31 from tempfile import TemporaryDirectory
31 from tempfile import TemporaryDirectory
32
32
33 from IPython.core import debugger
33 from IPython.core import debugger
34 from IPython.testing import decorators as dec
34 from IPython.testing import decorators as dec
35 from IPython.testing import tools as tt
35 from IPython.testing import tools as tt
36 from IPython.utils.io import capture_output
36 from IPython.utils.io import capture_output
37
37
38
38
39 def doctest_refbug():
39 def doctest_refbug():
40 """Very nasty problem with references held by multiple runs of a script.
40 """Very nasty problem with references held by multiple runs of a script.
41 See: https://github.com/ipython/ipython/issues/141
41 See: https://github.com/ipython/ipython/issues/141
42
42
43 In [1]: _ip.clear_main_mod_cache()
43 In [1]: _ip.clear_main_mod_cache()
44 # random
44 # random
45
45
46 In [2]: %run refbug
46 In [2]: %run refbug
47
47
48 In [3]: call_f()
48 In [3]: call_f()
49 lowercased: hello
49 lowercased: hello
50
50
51 In [4]: %run refbug
51 In [4]: %run refbug
52
52
53 In [5]: call_f()
53 In [5]: call_f()
54 lowercased: hello
54 lowercased: hello
55 lowercased: hello
55 lowercased: hello
56 """
56 """
57
57
58
58
59 def doctest_run_builtins():
59 def doctest_run_builtins():
60 r"""Check that %run doesn't damage __builtins__.
60 r"""Check that %run doesn't damage __builtins__.
61
61
62 In [1]: import tempfile
62 In [1]: import tempfile
63
63
64 In [2]: bid1 = id(__builtins__)
64 In [2]: bid1 = id(__builtins__)
65
65
66 In [3]: fname = tempfile.mkstemp('.py')[1]
66 In [3]: fname = tempfile.mkstemp('.py')[1]
67
67
68 In [3]: f = open(fname, 'w', encoding='utf-8')
68 In [3]: f = open(fname, 'w', encoding='utf-8')
69
69
70 In [4]: dummy= f.write('pass\n')
70 In [4]: dummy= f.write('pass\n')
71
71
72 In [5]: f.flush()
72 In [5]: f.flush()
73
73
74 In [6]: t1 = type(__builtins__)
74 In [6]: t1 = type(__builtins__)
75
75
76 In [7]: %run $fname
76 In [7]: %run $fname
77
77
78 In [7]: f.close()
78 In [7]: f.close()
79
79
80 In [8]: bid2 = id(__builtins__)
80 In [8]: bid2 = id(__builtins__)
81
81
82 In [9]: t2 = type(__builtins__)
82 In [9]: t2 = type(__builtins__)
83
83
84 In [10]: t1 == t2
84 In [10]: t1 == t2
85 Out[10]: True
85 Out[10]: True
86
86
87 In [10]: bid1 == bid2
87 In [10]: bid1 == bid2
88 Out[10]: True
88 Out[10]: True
89
89
90 In [12]: try:
90 In [12]: try:
91 ....: os.unlink(fname)
91 ....: os.unlink(fname)
92 ....: except:
92 ....: except:
93 ....: pass
93 ....: pass
94 ....:
94 ....:
95 """
95 """
96
96
97
97
98 def doctest_run_option_parser():
98 def doctest_run_option_parser():
99 r"""Test option parser in %run.
99 r"""Test option parser in %run.
100
100
101 In [1]: %run print_argv.py
101 In [1]: %run print_argv.py
102 []
102 []
103
103
104 In [2]: %run print_argv.py print*.py
104 In [2]: %run print_argv.py print*.py
105 ['print_argv.py']
105 ['print_argv.py']
106
106
107 In [3]: %run -G print_argv.py print*.py
107 In [3]: %run -G print_argv.py print*.py
108 ['print*.py']
108 ['print*.py']
109
109
110 """
110 """
111
111
112
112
113 @dec.skip_win32
113 @dec.skip_win32
114 def doctest_run_option_parser_for_posix():
114 def doctest_run_option_parser_for_posix():
115 r"""Test option parser in %run (Linux/OSX specific).
115 r"""Test option parser in %run (Linux/OSX specific).
116
116
117 You need double quote to escape glob in POSIX systems:
117 You need double quote to escape glob in POSIX systems:
118
118
119 In [1]: %run print_argv.py print\\*.py
119 In [1]: %run print_argv.py print\\*.py
120 ['print*.py']
120 ['print*.py']
121
121
122 You can't use quote to escape glob in POSIX systems:
122 You can't use quote to escape glob in POSIX systems:
123
123
124 In [2]: %run print_argv.py 'print*.py'
124 In [2]: %run print_argv.py 'print*.py'
125 ['print_argv.py']
125 ['print_argv.py']
126
126
127 """
127 """
128
128
129
129
130 doctest_run_option_parser_for_posix.__skip_doctest__ = sys.platform == "win32"
130 doctest_run_option_parser_for_posix.__skip_doctest__ = sys.platform == "win32"
131
131
132
132
133 @dec.skip_if_not_win32
133 @dec.skip_if_not_win32
134 def doctest_run_option_parser_for_windows():
134 def doctest_run_option_parser_for_windows():
135 r"""Test option parser in %run (Windows specific).
135 r"""Test option parser in %run (Windows specific).
136
136
137 In Windows, you can't escape ``*` `by backslash:
137 In Windows, you can't escape ``*` `by backslash:
138
138
139 In [1]: %run print_argv.py print\\*.py
139 In [1]: %run print_argv.py print\\*.py
140 ['print\\\\*.py']
140 ['print\\\\*.py']
141
141
142 You can use quote to escape glob:
142 You can use quote to escape glob:
143
143
144 In [2]: %run print_argv.py 'print*.py'
144 In [2]: %run print_argv.py 'print*.py'
145 ["'print*.py'"]
145 ["'print*.py'"]
146
146
147 """
147 """
148
148
149
149
150 doctest_run_option_parser_for_windows.__skip_doctest__ = sys.platform != "win32"
150 doctest_run_option_parser_for_windows.__skip_doctest__ = sys.platform != "win32"
151
151
152
152
153 def doctest_reset_del():
153 def doctest_reset_del():
154 """Test that resetting doesn't cause errors in __del__ methods.
154 """Test that resetting doesn't cause errors in __del__ methods.
155
155
156 In [2]: class A(object):
156 In [2]: class A(object):
157 ...: def __del__(self):
157 ...: def __del__(self):
158 ...: print(str("Hi"))
158 ...: print(str("Hi"))
159 ...:
159 ...:
160
160
161 In [3]: a = A()
161 In [3]: a = A()
162
162
163 In [4]: get_ipython().reset(); import gc; x = gc.collect(0)
163 In [4]: get_ipython().reset(); import gc; x = gc.collect(0)
164 Hi
164 Hi
165
165
166 In [5]: 1+1
166 In [5]: 1+1
167 Out[5]: 2
167 Out[5]: 2
168 """
168 """
169
169
170 # For some tests, it will be handy to organize them in a class with a common
170 # For some tests, it will be handy to organize them in a class with a common
171 # setup that makes a temp file
171 # setup that makes a temp file
172
172
173 class TestMagicRunPass(tt.TempFileMixin):
173 class TestMagicRunPass(tt.TempFileMixin):
174
174
175 def setUp(self):
175 def setUp(self):
176 content = "a = [1,2,3]\nb = 1"
176 content = "a = [1,2,3]\nb = 1"
177 self.mktmp(content)
177 self.mktmp(content)
178
178
179 def run_tmpfile(self):
179 def run_tmpfile(self):
180 _ip = get_ipython()
180 _ip = get_ipython()
181 # This fails on Windows if self.tmpfile.name has spaces or "~" in it.
181 # This fails on Windows if self.tmpfile.name has spaces or "~" in it.
182 # See below and ticket https://bugs.launchpad.net/bugs/366353
182 # See below and ticket https://bugs.launchpad.net/bugs/366353
183 _ip.run_line_magic("run", self.fname)
183 _ip.run_line_magic("run", self.fname)
184
184
185 def run_tmpfile_p(self):
185 def run_tmpfile_p(self):
186 _ip = get_ipython()
186 _ip = get_ipython()
187 # This fails on Windows if self.tmpfile.name has spaces or "~" in it.
187 # This fails on Windows if self.tmpfile.name has spaces or "~" in it.
188 # See below and ticket https://bugs.launchpad.net/bugs/366353
188 # See below and ticket https://bugs.launchpad.net/bugs/366353
189 _ip.run_line_magic("run", "-p %s" % self.fname)
189 _ip.run_line_magic("run", "-p %s" % self.fname)
190
190
191 def test_builtins_id(self):
191 def test_builtins_id(self):
192 """Check that %run doesn't damage __builtins__ """
192 """Check that %run doesn't damage __builtins__ """
193 _ip = get_ipython()
193 _ip = get_ipython()
194 # Test that the id of __builtins__ is not modified by %run
194 # Test that the id of __builtins__ is not modified by %run
195 bid1 = id(_ip.user_ns['__builtins__'])
195 bid1 = id(_ip.user_ns['__builtins__'])
196 self.run_tmpfile()
196 self.run_tmpfile()
197 bid2 = id(_ip.user_ns['__builtins__'])
197 bid2 = id(_ip.user_ns['__builtins__'])
198 assert bid1 == bid2
198 assert bid1 == bid2
199
199
200 def test_builtins_type(self):
200 def test_builtins_type(self):
201 """Check that the type of __builtins__ doesn't change with %run.
201 """Check that the type of __builtins__ doesn't change with %run.
202
202
203 However, the above could pass if __builtins__ was already modified to
203 However, the above could pass if __builtins__ was already modified to
204 be a dict (it should be a module) by a previous use of %run. So we
204 be a dict (it should be a module) by a previous use of %run. So we
205 also check explicitly that it really is a module:
205 also check explicitly that it really is a module:
206 """
206 """
207 _ip = get_ipython()
207 _ip = get_ipython()
208 self.run_tmpfile()
208 self.run_tmpfile()
209 assert type(_ip.user_ns["__builtins__"]) == type(sys)
209 assert type(_ip.user_ns["__builtins__"]) == type(sys)
210
210
211 def test_run_profile(self):
211 def test_run_profile(self):
212 """Test that the option -p, which invokes the profiler, do not
212 """Test that the option -p, which invokes the profiler, do not
213 crash by invoking execfile"""
213 crash by invoking execfile"""
214 self.run_tmpfile_p()
214 self.run_tmpfile_p()
215
215
216 def test_run_debug_twice(self):
216 def test_run_debug_twice(self):
217 # https://github.com/ipython/ipython/issues/10028
217 # https://github.com/ipython/ipython/issues/10028
218 _ip = get_ipython()
218 _ip = get_ipython()
219 with tt.fake_input(["c"]):
219 with tt.fake_input(["c"]):
220 _ip.run_line_magic("run", "-d %s" % self.fname)
220 _ip.run_line_magic("run", "-d %s" % self.fname)
221 with tt.fake_input(["c"]):
221 with tt.fake_input(["c"]):
222 _ip.run_line_magic("run", "-d %s" % self.fname)
222 _ip.run_line_magic("run", "-d %s" % self.fname)
223
223
224 def test_run_debug_twice_with_breakpoint(self):
224 def test_run_debug_twice_with_breakpoint(self):
225 """Make a valid python temp file."""
225 """Make a valid python temp file."""
226 _ip = get_ipython()
226 _ip = get_ipython()
227 with tt.fake_input(["b 2", "c", "c"]):
227 with tt.fake_input(["b 2", "c", "c"]):
228 _ip.run_line_magic("run", "-d %s" % self.fname)
228 _ip.run_line_magic("run", "-d %s" % self.fname)
229
229
230 with tt.fake_input(["c"]):
230 with tt.fake_input(["c"]):
231 with tt.AssertNotPrints("KeyError"):
231 with tt.AssertNotPrints("KeyError"):
232 _ip.run_line_magic("run", "-d %s" % self.fname)
232 _ip.run_line_magic("run", "-d %s" % self.fname)
233
233
234
234
235 class TestMagicRunSimple(tt.TempFileMixin):
235 class TestMagicRunSimple(tt.TempFileMixin):
236
236
237 def test_simpledef(self):
237 def test_simpledef(self):
238 """Test that simple class definitions work."""
238 """Test that simple class definitions work."""
239 src = ("class foo: pass\n"
239 src = ("class foo: pass\n"
240 "def f(): return foo()")
240 "def f(): return foo()")
241 self.mktmp(src)
241 self.mktmp(src)
242 _ip.run_line_magic("run", str(self.fname))
242 _ip.run_line_magic("run", str(self.fname))
243 _ip.run_cell("t = isinstance(f(), foo)")
243 _ip.run_cell("t = isinstance(f(), foo)")
244 assert _ip.user_ns["t"] is True
244 assert _ip.user_ns["t"] is True
245
245
246 @pytest.mark.xfail(
246 @pytest.mark.xfail(
247 platform.python_implementation() == "PyPy",
247 platform.python_implementation() == "PyPy",
248 reason="expecting __del__ call on exit is unreliable and doesn't happen on PyPy",
248 reason="expecting __del__ call on exit is unreliable and doesn't happen on PyPy",
249 )
249 )
250 def test_obj_del(self):
250 def test_obj_del(self):
251 """Test that object's __del__ methods are called on exit."""
251 """Test that object's __del__ methods are called on exit."""
252 src = ("class A(object):\n"
252 src = ("class A(object):\n"
253 " def __del__(self):\n"
253 " def __del__(self):\n"
254 " print('object A deleted')\n"
254 " print('object A deleted')\n"
255 "a = A()\n")
255 "a = A()\n")
256 self.mktmp(src)
256 self.mktmp(src)
257 err = None
257 err = None
258 tt.ipexec_validate(self.fname, 'object A deleted', err)
258 tt.ipexec_validate(self.fname, 'object A deleted', err)
259
259
260 def test_aggressive_namespace_cleanup(self):
260 def test_aggressive_namespace_cleanup(self):
261 """Test that namespace cleanup is not too aggressive GH-238
261 """Test that namespace cleanup is not too aggressive GH-238
262
262
263 Returning from another run magic deletes the namespace"""
263 Returning from another run magic deletes the namespace"""
264 # see ticket https://github.com/ipython/ipython/issues/238
264 # see ticket https://github.com/ipython/ipython/issues/238
265
265
266 with tt.TempFileMixin() as empty:
266 with tt.TempFileMixin() as empty:
267 empty.mktmp("")
267 empty.mktmp("")
268 # On Windows, the filename will have \users in it, so we need to use the
268 # On Windows, the filename will have \users in it, so we need to use the
269 # repr so that the \u becomes \\u.
269 # repr so that the \u becomes \\u.
270 src = (
270 src = (
271 "ip = get_ipython()\n"
271 "ip = get_ipython()\n"
272 "for i in range(5):\n"
272 "for i in range(5):\n"
273 " try:\n"
273 " try:\n"
274 " ip.magic(%r)\n"
274 " ip.magic(%r)\n"
275 " except NameError as e:\n"
275 " except NameError as e:\n"
276 " print(i)\n"
276 " print(i)\n"
277 " break\n" % ("run " + empty.fname)
277 " break\n" % ("run " + empty.fname)
278 )
278 )
279 self.mktmp(src)
279 self.mktmp(src)
280 _ip.run_line_magic("run", str(self.fname))
280 _ip.run_line_magic("run", str(self.fname))
281 _ip.run_cell("ip == get_ipython()")
281 _ip.run_cell("ip == get_ipython()")
282 assert _ip.user_ns["i"] == 4
282 assert _ip.user_ns["i"] == 4
283
283
284 def test_run_second(self):
284 def test_run_second(self):
285 """Test that running a second file doesn't clobber the first, gh-3547"""
285 """Test that running a second file doesn't clobber the first, gh-3547"""
286 self.mktmp("avar = 1\n" "def afunc():\n" " return avar\n")
286 self.mktmp("avar = 1\n" "def afunc():\n" " return avar\n")
287
287
288 with tt.TempFileMixin() as empty:
288 with tt.TempFileMixin() as empty:
289 empty.mktmp("")
289 empty.mktmp("")
290
290
291 _ip.run_line_magic("run", self.fname)
291 _ip.run_line_magic("run", self.fname)
292 _ip.run_line_magic("run", empty.fname)
292 _ip.run_line_magic("run", empty.fname)
293 assert _ip.user_ns["afunc"]() == 1
293 assert _ip.user_ns["afunc"]() == 1
294
294
295 def test_tclass(self):
295 def test_tclass(self):
296 mydir = os.path.dirname(__file__)
296 mydir = os.path.dirname(__file__)
297 tc = os.path.join(mydir, "tclass")
297 tc = os.path.join(mydir, "tclass")
298 src = f"""\
298 src = f"""\
299 import gc
299 import gc
300 %run "{tc}" C-first
300 %run "{tc}" C-first
301 gc.collect(0)
301 gc.collect(0)
302 %run "{tc}" C-second
302 %run "{tc}" C-second
303 gc.collect(0)
303 gc.collect(0)
304 %run "{tc}" C-third
304 %run "{tc}" C-third
305 gc.collect(0)
305 gc.collect(0)
306 %reset -f
306 %reset -f
307 """
307 """
308 self.mktmp(src, ".ipy")
308 self.mktmp(src, ".ipy")
309 out = """\
309 out = """\
310 ARGV 1-: ['C-first']
310 ARGV 1-: ['C-first']
311 ARGV 1-: ['C-second']
311 ARGV 1-: ['C-second']
312 tclass.py: deleting object: C-first
312 tclass.py: deleting object: C-first
313 ARGV 1-: ['C-third']
313 ARGV 1-: ['C-third']
314 tclass.py: deleting object: C-second
314 tclass.py: deleting object: C-second
315 tclass.py: deleting object: C-third
315 tclass.py: deleting object: C-third
316 """
316 """
317 err = None
317 err = None
318 tt.ipexec_validate(self.fname, out, err)
318 tt.ipexec_validate(self.fname, out, err)
319
319
320 def test_run_i_after_reset(self):
320 def test_run_i_after_reset(self):
321 """Check that %run -i still works after %reset (gh-693)"""
321 """Check that %run -i still works after %reset (gh-693)"""
322 src = "yy = zz\n"
322 src = "yy = zz\n"
323 self.mktmp(src)
323 self.mktmp(src)
324 _ip.run_cell("zz = 23")
324 _ip.run_cell("zz = 23")
325 try:
325 try:
326 _ip.run_line_magic("run", "-i %s" % self.fname)
326 _ip.run_line_magic("run", "-i %s" % self.fname)
327 assert _ip.user_ns["yy"] == 23
327 assert _ip.user_ns["yy"] == 23
328 finally:
328 finally:
329 _ip.run_line_magic("reset", "-f")
329 _ip.run_line_magic("reset", "-f")
330
330
331 _ip.run_cell("zz = 23")
331 _ip.run_cell("zz = 23")
332 try:
332 try:
333 _ip.run_line_magic("run", "-i %s" % self.fname)
333 _ip.run_line_magic("run", "-i %s" % self.fname)
334 assert _ip.user_ns["yy"] == 23
334 assert _ip.user_ns["yy"] == 23
335 finally:
335 finally:
336 _ip.run_line_magic("reset", "-f")
336 _ip.run_line_magic("reset", "-f")
337
337
338 def test_unicode(self):
338 def test_unicode(self):
339 """Check that files in odd encodings are accepted."""
339 """Check that files in odd encodings are accepted."""
340 mydir = os.path.dirname(__file__)
340 mydir = os.path.dirname(__file__)
341 na = os.path.join(mydir, "nonascii.py")
341 na = os.path.join(mydir, "nonascii.py")
342 _ip.magic('run "%s"' % na)
342 _ip.magic('run "%s"' % na)
343 assert _ip.user_ns["u"] == "ΠŽΡ‚β„–Π€"
343 assert _ip.user_ns["u"] == "ΠŽΡ‚β„–Π€"
344
344
345 def test_run_py_file_attribute(self):
345 def test_run_py_file_attribute(self):
346 """Test handling of `__file__` attribute in `%run <file>.py`."""
346 """Test handling of `__file__` attribute in `%run <file>.py`."""
347 src = "t = __file__\n"
347 src = "t = __file__\n"
348 self.mktmp(src)
348 self.mktmp(src)
349 _missing = object()
349 _missing = object()
350 file1 = _ip.user_ns.get("__file__", _missing)
350 file1 = _ip.user_ns.get("__file__", _missing)
351 _ip.run_line_magic("run", self.fname)
351 _ip.run_line_magic("run", self.fname)
352 file2 = _ip.user_ns.get("__file__", _missing)
352 file2 = _ip.user_ns.get("__file__", _missing)
353
353
354 # Check that __file__ was equal to the filename in the script's
354 # Check that __file__ was equal to the filename in the script's
355 # namespace.
355 # namespace.
356 assert _ip.user_ns["t"] == self.fname
356 assert _ip.user_ns["t"] == self.fname
357
357
358 # Check that __file__ was not leaked back into user_ns.
358 # Check that __file__ was not leaked back into user_ns.
359 assert file1 == file2
359 assert file1 == file2
360
360
361 def test_run_ipy_file_attribute(self):
361 def test_run_ipy_file_attribute(self):
362 """Test handling of `__file__` attribute in `%run <file.ipy>`."""
362 """Test handling of `__file__` attribute in `%run <file.ipy>`."""
363 src = "t = __file__\n"
363 src = "t = __file__\n"
364 self.mktmp(src, ext='.ipy')
364 self.mktmp(src, ext='.ipy')
365 _missing = object()
365 _missing = object()
366 file1 = _ip.user_ns.get("__file__", _missing)
366 file1 = _ip.user_ns.get("__file__", _missing)
367 _ip.run_line_magic("run", self.fname)
367 _ip.run_line_magic("run", self.fname)
368 file2 = _ip.user_ns.get("__file__", _missing)
368 file2 = _ip.user_ns.get("__file__", _missing)
369
369
370 # Check that __file__ was equal to the filename in the script's
370 # Check that __file__ was equal to the filename in the script's
371 # namespace.
371 # namespace.
372 assert _ip.user_ns["t"] == self.fname
372 assert _ip.user_ns["t"] == self.fname
373
373
374 # Check that __file__ was not leaked back into user_ns.
374 # Check that __file__ was not leaked back into user_ns.
375 assert file1 == file2
375 assert file1 == file2
376
376
377 def test_run_formatting(self):
377 def test_run_formatting(self):
378 """ Test that %run -t -N<N> does not raise a TypeError for N > 1."""
378 """ Test that %run -t -N<N> does not raise a TypeError for N > 1."""
379 src = "pass"
379 src = "pass"
380 self.mktmp(src)
380 self.mktmp(src)
381 _ip.run_line_magic("run", "-t -N 1 %s" % self.fname)
381 _ip.run_line_magic("run", "-t -N 1 %s" % self.fname)
382 _ip.run_line_magic("run", "-t -N 10 %s" % self.fname)
382 _ip.run_line_magic("run", "-t -N 10 %s" % self.fname)
383
383
384 def test_ignore_sys_exit(self):
384 def test_ignore_sys_exit(self):
385 """Test the -e option to ignore sys.exit()"""
385 """Test the -e option to ignore sys.exit()"""
386 src = "import sys; sys.exit(1)"
386 src = "import sys; sys.exit(1)"
387 self.mktmp(src)
387 self.mktmp(src)
388 with tt.AssertPrints("SystemExit"):
388 with tt.AssertPrints("SystemExit"):
389 _ip.run_line_magic("run", self.fname)
389 _ip.run_line_magic("run", self.fname)
390
390
391 with tt.AssertNotPrints("SystemExit"):
391 with tt.AssertNotPrints("SystemExit"):
392 _ip.run_line_magic("run", "-e %s" % self.fname)
392 _ip.run_line_magic("run", "-e %s" % self.fname)
393
393
394 def test_run_nb(self):
394 def test_run_nb(self):
395 """Test %run notebook.ipynb"""
395 """Test %run notebook.ipynb"""
396 pytest.importorskip("nbformat")
396 pytest.importorskip("nbformat")
397 from nbformat import v4, writes
397 from nbformat import v4, writes
398 nb = v4.new_notebook(
398 nb = v4.new_notebook(
399 cells=[
399 cells=[
400 v4.new_markdown_cell("The Ultimate Question of Everything"),
400 v4.new_markdown_cell("The Ultimate Question of Everything"),
401 v4.new_code_cell("answer=42")
401 v4.new_code_cell("answer=42")
402 ]
402 ]
403 )
403 )
404 src = writes(nb, version=4)
404 src = writes(nb, version=4)
405 self.mktmp(src, ext='.ipynb')
405 self.mktmp(src, ext='.ipynb')
406
406
407 _ip.run_line_magic("run", self.fname)
407 _ip.run_line_magic("run", self.fname)
408
408
409 assert _ip.user_ns["answer"] == 42
409 assert _ip.user_ns["answer"] == 42
410
410
411 def test_run_nb_error(self):
411 def test_run_nb_error(self):
412 """Test %run notebook.ipynb error"""
412 """Test %run notebook.ipynb error"""
413 pytest.importorskip("nbformat")
413 pytest.importorskip("nbformat")
414 from nbformat import v4, writes
414 from nbformat import v4, writes
415
415
416 # %run when a file name isn't provided
416 # %run when a file name isn't provided
417 pytest.raises(Exception, _ip.magic, "run")
417 pytest.raises(Exception, _ip.magic, "run")
418
418
419 # %run when a file doesn't exist
419 # %run when a file doesn't exist
420 pytest.raises(Exception, _ip.magic, "run foobar.ipynb")
420 pytest.raises(Exception, _ip.magic, "run foobar.ipynb")
421
421
422 # %run on a notebook with an error
422 # %run on a notebook with an error
423 nb = v4.new_notebook(
423 nb = v4.new_notebook(
424 cells=[
424 cells=[
425 v4.new_code_cell("0/0")
425 v4.new_code_cell("0/0")
426 ]
426 ]
427 )
427 )
428 src = writes(nb, version=4)
428 src = writes(nb, version=4)
429 self.mktmp(src, ext='.ipynb')
429 self.mktmp(src, ext='.ipynb')
430 pytest.raises(Exception, _ip.magic, "run %s" % self.fname)
430 pytest.raises(Exception, _ip.magic, "run %s" % self.fname)
431
431
432 def test_file_options(self):
432 def test_file_options(self):
433 src = ('import sys\n'
433 src = ('import sys\n'
434 'a = " ".join(sys.argv[1:])\n')
434 'a = " ".join(sys.argv[1:])\n')
435 self.mktmp(src)
435 self.mktmp(src)
436 test_opts = "-x 3 --verbose"
436 test_opts = "-x 3 --verbose"
437 _ip.run_line_magic("run", "{0} {1}".format(self.fname, test_opts))
437 _ip.run_line_magic("run", "{0} {1}".format(self.fname, test_opts))
438 assert _ip.user_ns["a"] == test_opts
438 assert _ip.user_ns["a"] == test_opts
439
439
440
440
441 class TestMagicRunWithPackage(unittest.TestCase):
441 class TestMagicRunWithPackage(unittest.TestCase):
442
442
443 def writefile(self, name, content):
443 def writefile(self, name, content):
444 path = os.path.join(self.tempdir.name, name)
444 path = os.path.join(self.tempdir.name, name)
445 d = os.path.dirname(path)
445 d = os.path.dirname(path)
446 if not os.path.isdir(d):
446 if not os.path.isdir(d):
447 os.makedirs(d)
447 os.makedirs(d)
448 with open(path, "w", encoding="utf-8") as f:
448 with open(path, "w", encoding="utf-8") as f:
449 f.write(textwrap.dedent(content))
449 f.write(textwrap.dedent(content))
450
450
451 def setUp(self):
451 def setUp(self):
452 self.package = package = 'tmp{0}'.format(''.join([random.choice(string.ascii_letters) for i in range(10)]))
452 self.package = package = 'tmp{0}'.format(''.join([random.choice(string.ascii_letters) for i in range(10)]))
453 """Temporary (probably) valid python package name."""
453 """Temporary (probably) valid python package name."""
454
454
455 self.value = int(random.random() * 10000)
455 self.value = int(random.random() * 10000)
456
456
457 self.tempdir = TemporaryDirectory()
457 self.tempdir = TemporaryDirectory()
458 self.__orig_cwd = os.getcwd()
458 self.__orig_cwd = os.getcwd()
459 sys.path.insert(0, self.tempdir.name)
459 sys.path.insert(0, self.tempdir.name)
460
460
461 self.writefile(os.path.join(package, '__init__.py'), '')
461 self.writefile(os.path.join(package, '__init__.py'), '')
462 self.writefile(os.path.join(package, 'sub.py'), """
462 self.writefile(os.path.join(package, 'sub.py'), """
463 x = {0!r}
463 x = {0!r}
464 """.format(self.value))
464 """.format(self.value))
465 self.writefile(os.path.join(package, 'relative.py'), """
465 self.writefile(os.path.join(package, 'relative.py'), """
466 from .sub import x
466 from .sub import x
467 """)
467 """)
468 self.writefile(os.path.join(package, 'absolute.py'), """
468 self.writefile(os.path.join(package, 'absolute.py'), """
469 from {0}.sub import x
469 from {0}.sub import x
470 """.format(package))
470 """.format(package))
471 self.writefile(os.path.join(package, 'args.py'), """
471 self.writefile(os.path.join(package, 'args.py'), """
472 import sys
472 import sys
473 a = " ".join(sys.argv[1:])
473 a = " ".join(sys.argv[1:])
474 """.format(package))
474 """.format(package))
475
475
476 def tearDown(self):
476 def tearDown(self):
477 os.chdir(self.__orig_cwd)
477 os.chdir(self.__orig_cwd)
478 sys.path[:] = [p for p in sys.path if p != self.tempdir.name]
478 sys.path[:] = [p for p in sys.path if p != self.tempdir.name]
479 self.tempdir.cleanup()
479 self.tempdir.cleanup()
480
480
481 def check_run_submodule(self, submodule, opts=""):
481 def check_run_submodule(self, submodule, opts=""):
482 _ip.user_ns.pop("x", None)
482 _ip.user_ns.pop("x", None)
483 _ip.run_line_magic(
483 _ip.run_line_magic(
484 "run", "{2} -m {0}.{1}".format(self.package, submodule, opts)
484 "run", "{2} -m {0}.{1}".format(self.package, submodule, opts)
485 )
485 )
486 self.assertEqual(
486 self.assertEqual(
487 _ip.user_ns["x"],
487 _ip.user_ns["x"],
488 self.value,
488 self.value,
489 "Variable `x` is not loaded from module `{0}`.".format(submodule),
489 "Variable `x` is not loaded from module `{0}`.".format(submodule),
490 )
490 )
491
491
492 def test_run_submodule_with_absolute_import(self):
492 def test_run_submodule_with_absolute_import(self):
493 self.check_run_submodule('absolute')
493 self.check_run_submodule('absolute')
494
494
495 def test_run_submodule_with_relative_import(self):
495 def test_run_submodule_with_relative_import(self):
496 """Run submodule that has a relative import statement (#2727)."""
496 """Run submodule that has a relative import statement (#2727)."""
497 self.check_run_submodule('relative')
497 self.check_run_submodule('relative')
498
498
499 def test_prun_submodule_with_absolute_import(self):
499 def test_prun_submodule_with_absolute_import(self):
500 self.check_run_submodule('absolute', '-p')
500 self.check_run_submodule('absolute', '-p')
501
501
502 def test_prun_submodule_with_relative_import(self):
502 def test_prun_submodule_with_relative_import(self):
503 self.check_run_submodule('relative', '-p')
503 self.check_run_submodule('relative', '-p')
504
504
505 def with_fake_debugger(func):
505 def with_fake_debugger(func):
506 @functools.wraps(func)
506 @functools.wraps(func)
507 def wrapper(*args, **kwds):
507 def wrapper(*args, **kwds):
508 with patch.object(debugger.Pdb, 'run', staticmethod(eval)):
508 with patch.object(debugger.Pdb, 'run', staticmethod(eval)):
509 return func(*args, **kwds)
509 return func(*args, **kwds)
510 return wrapper
510 return wrapper
511
511
512 @with_fake_debugger
512 @with_fake_debugger
513 def test_debug_run_submodule_with_absolute_import(self):
513 def test_debug_run_submodule_with_absolute_import(self):
514 self.check_run_submodule('absolute', '-d')
514 self.check_run_submodule('absolute', '-d')
515
515
516 @with_fake_debugger
516 @with_fake_debugger
517 def test_debug_run_submodule_with_relative_import(self):
517 def test_debug_run_submodule_with_relative_import(self):
518 self.check_run_submodule('relative', '-d')
518 self.check_run_submodule('relative', '-d')
519
519
520 def test_module_options(self):
520 def test_module_options(self):
521 _ip.user_ns.pop("a", None)
521 _ip.user_ns.pop("a", None)
522 test_opts = "-x abc -m test"
522 test_opts = "-x abc -m test"
523 _ip.run_line_magic("run", "-m {0}.args {1}".format(self.package, test_opts))
523 _ip.run_line_magic("run", "-m {0}.args {1}".format(self.package, test_opts))
524 assert _ip.user_ns["a"] == test_opts
524 assert _ip.user_ns["a"] == test_opts
525
525
526 def test_module_options_with_separator(self):
526 def test_module_options_with_separator(self):
527 _ip.user_ns.pop("a", None)
527 _ip.user_ns.pop("a", None)
528 test_opts = "-x abc -m test"
528 test_opts = "-x abc -m test"
529 _ip.run_line_magic("run", "-m {0}.args -- {1}".format(self.package, test_opts))
529 _ip.run_line_magic("run", "-m {0}.args -- {1}".format(self.package, test_opts))
530 assert _ip.user_ns["a"] == test_opts
530 assert _ip.user_ns["a"] == test_opts
531
531
532
532
533 def test_run__name__():
533 def test_run__name__():
534 with TemporaryDirectory() as td:
534 with TemporaryDirectory() as td:
535 path = pjoin(td, "foo.py")
535 path = pjoin(td, "foo.py")
536 with open(path, "w", encoding="utf-8") as f:
536 with open(path, "w", encoding="utf-8") as f:
537 f.write("q = __name__")
537 f.write("q = __name__")
538
538
539 _ip.user_ns.pop("q", None)
539 _ip.user_ns.pop("q", None)
540 _ip.run_line_magic("run", "{}".format(path))
540 _ip.run_line_magic("run", "{}".format(path))
541 assert _ip.user_ns.pop("q") == "__main__"
541 assert _ip.user_ns.pop("q") == "__main__"
542
542
543 _ip.run_line_magic("run", "-n {}".format(path))
543 _ip.run_line_magic("run", "-n {}".format(path))
544 assert _ip.user_ns.pop("q") == "foo"
544 assert _ip.user_ns.pop("q") == "foo"
545
545
546 try:
546 try:
547 _ip.run_line_magic("run", "-i -n {}".format(path))
547 _ip.run_line_magic("run", "-i -n {}".format(path))
548 assert _ip.user_ns.pop("q") == "foo"
548 assert _ip.user_ns.pop("q") == "foo"
549 finally:
549 finally:
550 _ip.run_line_magic("reset", "-f")
550 _ip.run_line_magic("reset", "-f")
551
551
552
552
553 def test_run_tb():
553 def test_run_tb():
554 """Test traceback offset in %run"""
554 """Test traceback offset in %run"""
555 with TemporaryDirectory() as td:
555 with TemporaryDirectory() as td:
556 path = pjoin(td, "foo.py")
556 path = pjoin(td, "foo.py")
557 with open(path, "w", encoding="utf-8") as f:
557 with open(path, "w", encoding="utf-8") as f:
558 f.write(
558 f.write(
559 "\n".join(
559 "\n".join(
560 [
560 [
561 "def foo():",
561 "def foo():",
562 " return bar()",
562 " return bar()",
563 "def bar():",
563 "def bar():",
564 " raise RuntimeError('hello!')",
564 " raise RuntimeError('hello!')",
565 "foo()",
565 "foo()",
566 ]
566 ]
567 )
567 )
568 )
568 )
569 with capture_output() as io:
569 with capture_output() as io:
570 _ip.run_line_magic("run", "{}".format(path))
570 _ip.run_line_magic("run", "{}".format(path))
571 out = io.stdout
571 out = io.stdout
572 assert "execfile" not in out
572 assert "execfile" not in out
573 assert "RuntimeError" in out
573 assert "RuntimeError" in out
574 assert out.count("---->") == 3
574 assert out.count("---->") == 3
575 del ip.user_ns['bar']
575 del ip.user_ns['bar']
576 del ip.user_ns['foo']
576 del ip.user_ns['foo']
577
577
578
578
579 def test_multiprocessing_run():
579 def test_multiprocessing_run():
580 """Set we can run mutiprocesgin without messing up up main namespace
580 """Set we can run mutiprocesgin without messing up up main namespace
581
581
582 Note that import `nose.tools as nt` mdify the value s
582 Note that import `nose.tools as nt` modify the values
583 sys.module['__mp_main__'] so we need to temporarily set it to None to test
583 sys.module['__mp_main__'] so we need to temporarily set it to None to test
584 the issue.
584 the issue.
585 """
585 """
586 with TemporaryDirectory() as td:
586 with TemporaryDirectory() as td:
587 mpm = sys.modules.get('__mp_main__')
587 mpm = sys.modules.get('__mp_main__')
588 sys.modules['__mp_main__'] = None
588 sys.modules['__mp_main__'] = None
589 try:
589 try:
590 path = pjoin(td, "test.py")
590 path = pjoin(td, "test.py")
591 with open(path, "w", encoding="utf-8") as f:
591 with open(path, "w", encoding="utf-8") as f:
592 f.write("import multiprocessing\nprint('hoy')")
592 f.write("import multiprocessing\nprint('hoy')")
593 with capture_output() as io:
593 with capture_output() as io:
594 _ip.run_line_magic('run', path)
594 _ip.run_line_magic('run', path)
595 _ip.run_cell("i_m_undefined")
595 _ip.run_cell("i_m_undefined")
596 out = io.stdout
596 out = io.stdout
597 assert "hoy" in out
597 assert "hoy" in out
598 assert "AttributeError" not in out
598 assert "AttributeError" not in out
599 assert "NameError" in out
599 assert "NameError" in out
600 assert out.count("---->") == 1
600 assert out.count("---->") == 1
601 except:
601 except:
602 raise
602 raise
603 finally:
603 finally:
604 sys.modules['__mp_main__'] = mpm
604 sys.modules['__mp_main__'] = mpm
605
605
606
606
607 def test_script_tb():
607 def test_script_tb():
608 """Test traceback offset in `ipython script.py`"""
608 """Test traceback offset in `ipython script.py`"""
609 with TemporaryDirectory() as td:
609 with TemporaryDirectory() as td:
610 path = pjoin(td, "foo.py")
610 path = pjoin(td, "foo.py")
611 with open(path, "w", encoding="utf-8") as f:
611 with open(path, "w", encoding="utf-8") as f:
612 f.write(
612 f.write(
613 "\n".join(
613 "\n".join(
614 [
614 [
615 "def foo():",
615 "def foo():",
616 " return bar()",
616 " return bar()",
617 "def bar():",
617 "def bar():",
618 " raise RuntimeError('hello!')",
618 " raise RuntimeError('hello!')",
619 "foo()",
619 "foo()",
620 ]
620 ]
621 )
621 )
622 )
622 )
623 out, err = tt.ipexec(path)
623 out, err = tt.ipexec(path)
624 assert "execfile" not in out
624 assert "execfile" not in out
625 assert "RuntimeError" in out
625 assert "RuntimeError" in out
626 assert out.count("---->") == 3
626 assert out.count("---->") == 3
@@ -1,456 +1,456
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 os.path
5 import os.path
6 import platform
6 import platform
7 import re
7 import re
8 import sys
8 import sys
9 import traceback
9 import traceback
10 import unittest
10 import unittest
11 from textwrap import dedent
11 from textwrap import dedent
12
12
13 from tempfile import TemporaryDirectory
13 from tempfile import TemporaryDirectory
14
14
15 from IPython.core.ultratb import ColorTB, VerboseTB
15 from IPython.core.ultratb import ColorTB, VerboseTB
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, skip_without
17 from IPython.testing.decorators import onlyif_unicode_paths, skip_without
18 from IPython.utils.syspathcontext import prepended_to_syspath
18 from IPython.utils.syspathcontext import prepended_to_syspath
19
19
20 file_1 = """1
20 file_1 = """1
21 2
21 2
22 3
22 3
23 def f():
23 def f():
24 1/0
24 1/0
25 """
25 """
26
26
27 file_2 = """def f():
27 file_2 = """def f():
28 1/0
28 1/0
29 """
29 """
30
30
31
31
32 def recursionlimit(frames):
32 def recursionlimit(frames):
33 """
33 """
34 decorator to set the recursion limit temporarily
34 decorator to set the recursion limit temporarily
35 """
35 """
36
36
37 def inner(test_function):
37 def inner(test_function):
38 def wrapper(*args, **kwargs):
38 def wrapper(*args, **kwargs):
39 rl = sys.getrecursionlimit()
39 rl = sys.getrecursionlimit()
40 sys.setrecursionlimit(frames)
40 sys.setrecursionlimit(frames)
41 try:
41 try:
42 return test_function(*args, **kwargs)
42 return test_function(*args, **kwargs)
43 finally:
43 finally:
44 sys.setrecursionlimit(rl)
44 sys.setrecursionlimit(rl)
45
45
46 return wrapper
46 return wrapper
47
47
48 return inner
48 return inner
49
49
50
50
51 class ChangedPyFileTest(unittest.TestCase):
51 class ChangedPyFileTest(unittest.TestCase):
52 def test_changing_py_file(self):
52 def test_changing_py_file(self):
53 """Traceback produced if the line where the error occurred is missing?
53 """Traceback produced if the line where the error occurred is missing?
54
54
55 https://github.com/ipython/ipython/issues/1456
55 https://github.com/ipython/ipython/issues/1456
56 """
56 """
57 with TemporaryDirectory() as td:
57 with TemporaryDirectory() as td:
58 fname = os.path.join(td, "foo.py")
58 fname = os.path.join(td, "foo.py")
59 with open(fname, "w", encoding="utf-8") as f:
59 with open(fname, "w", encoding="utf-8") as f:
60 f.write(file_1)
60 f.write(file_1)
61
61
62 with prepended_to_syspath(td):
62 with prepended_to_syspath(td):
63 ip.run_cell("import foo")
63 ip.run_cell("import foo")
64
64
65 with tt.AssertPrints("ZeroDivisionError"):
65 with tt.AssertPrints("ZeroDivisionError"):
66 ip.run_cell("foo.f()")
66 ip.run_cell("foo.f()")
67
67
68 # Make the file shorter, so the line of the error is missing.
68 # Make the file shorter, so the line of the error is missing.
69 with open(fname, "w", encoding="utf-8") as f:
69 with open(fname, "w", encoding="utf-8") as f:
70 f.write(file_2)
70 f.write(file_2)
71
71
72 # For some reason, this was failing on the *second* call after
72 # For some reason, this was failing on the *second* call after
73 # changing the file, so we call f() twice.
73 # changing the file, so we call f() twice.
74 with tt.AssertNotPrints("Internal Python error", channel='stderr'):
74 with tt.AssertNotPrints("Internal Python error", channel='stderr'):
75 with tt.AssertPrints("ZeroDivisionError"):
75 with tt.AssertPrints("ZeroDivisionError"):
76 ip.run_cell("foo.f()")
76 ip.run_cell("foo.f()")
77 with tt.AssertPrints("ZeroDivisionError"):
77 with tt.AssertPrints("ZeroDivisionError"):
78 ip.run_cell("foo.f()")
78 ip.run_cell("foo.f()")
79
79
80 iso_8859_5_file = u'''# coding: iso-8859-5
80 iso_8859_5_file = u'''# coding: iso-8859-5
81
81
82 def fail():
82 def fail():
83 """Π΄Π±Π˜Π–"""
83 """Π΄Π±Π˜Π–"""
84 1/0 # Π΄Π±Π˜Π–
84 1/0 # Π΄Π±Π˜Π–
85 '''
85 '''
86
86
87 class NonAsciiTest(unittest.TestCase):
87 class NonAsciiTest(unittest.TestCase):
88 @onlyif_unicode_paths
88 @onlyif_unicode_paths
89 def test_nonascii_path(self):
89 def test_nonascii_path(self):
90 # Non-ascii directory name as well.
90 # Non-ascii directory name as well.
91 with TemporaryDirectory(suffix=u'Γ©') as td:
91 with TemporaryDirectory(suffix=u'Γ©') as td:
92 fname = os.path.join(td, u"fooΓ©.py")
92 fname = os.path.join(td, u"fooΓ©.py")
93 with open(fname, "w", encoding="utf-8") as f:
93 with open(fname, "w", encoding="utf-8") as f:
94 f.write(file_1)
94 f.write(file_1)
95
95
96 with prepended_to_syspath(td):
96 with prepended_to_syspath(td):
97 ip.run_cell("import foo")
97 ip.run_cell("import foo")
98
98
99 with tt.AssertPrints("ZeroDivisionError"):
99 with tt.AssertPrints("ZeroDivisionError"):
100 ip.run_cell("foo.f()")
100 ip.run_cell("foo.f()")
101
101
102 def test_iso8859_5(self):
102 def test_iso8859_5(self):
103 with TemporaryDirectory() as td:
103 with TemporaryDirectory() as td:
104 fname = os.path.join(td, 'dfghjkl.py')
104 fname = os.path.join(td, 'dfghjkl.py')
105
105
106 with io.open(fname, 'w', encoding='iso-8859-5') as f:
106 with io.open(fname, 'w', encoding='iso-8859-5') as f:
107 f.write(iso_8859_5_file)
107 f.write(iso_8859_5_file)
108
108
109 with prepended_to_syspath(td):
109 with prepended_to_syspath(td):
110 ip.run_cell("from dfghjkl import fail")
110 ip.run_cell("from dfghjkl import fail")
111
111
112 with tt.AssertPrints("ZeroDivisionError"):
112 with tt.AssertPrints("ZeroDivisionError"):
113 with tt.AssertPrints(u'Π΄Π±Π˜Π–', suppress=False):
113 with tt.AssertPrints(u'Π΄Π±Π˜Π–', suppress=False):
114 ip.run_cell('fail()')
114 ip.run_cell('fail()')
115
115
116 def test_nonascii_msg(self):
116 def test_nonascii_msg(self):
117 cell = u"raise Exception('Γ©')"
117 cell = u"raise Exception('Γ©')"
118 expected = u"Exception('Γ©')"
118 expected = u"Exception('Γ©')"
119 ip.run_cell("%xmode plain")
119 ip.run_cell("%xmode plain")
120 with tt.AssertPrints(expected):
120 with tt.AssertPrints(expected):
121 ip.run_cell(cell)
121 ip.run_cell(cell)
122
122
123 ip.run_cell("%xmode verbose")
123 ip.run_cell("%xmode verbose")
124 with tt.AssertPrints(expected):
124 with tt.AssertPrints(expected):
125 ip.run_cell(cell)
125 ip.run_cell(cell)
126
126
127 ip.run_cell("%xmode context")
127 ip.run_cell("%xmode context")
128 with tt.AssertPrints(expected):
128 with tt.AssertPrints(expected):
129 ip.run_cell(cell)
129 ip.run_cell(cell)
130
130
131 ip.run_cell("%xmode minimal")
131 ip.run_cell("%xmode minimal")
132 with tt.AssertPrints(u"Exception: Γ©"):
132 with tt.AssertPrints(u"Exception: Γ©"):
133 ip.run_cell(cell)
133 ip.run_cell(cell)
134
134
135 # Put this back into Context mode for later tests.
135 # Put this back into Context mode for later tests.
136 ip.run_cell("%xmode context")
136 ip.run_cell("%xmode context")
137
137
138 class NestedGenExprTestCase(unittest.TestCase):
138 class NestedGenExprTestCase(unittest.TestCase):
139 """
139 """
140 Regression test for the following issues:
140 Regression test for the following issues:
141 https://github.com/ipython/ipython/issues/8293
141 https://github.com/ipython/ipython/issues/8293
142 https://github.com/ipython/ipython/issues/8205
142 https://github.com/ipython/ipython/issues/8205
143 """
143 """
144 def test_nested_genexpr(self):
144 def test_nested_genexpr(self):
145 code = dedent(
145 code = dedent(
146 """\
146 """\
147 class SpecificException(Exception):
147 class SpecificException(Exception):
148 pass
148 pass
149
149
150 def foo(x):
150 def foo(x):
151 raise SpecificException("Success!")
151 raise SpecificException("Success!")
152
152
153 sum(sum(foo(x) for _ in [0]) for x in [0])
153 sum(sum(foo(x) for _ in [0]) for x in [0])
154 """
154 """
155 )
155 )
156 with tt.AssertPrints('SpecificException: Success!', suppress=False):
156 with tt.AssertPrints('SpecificException: Success!', suppress=False):
157 ip.run_cell(code)
157 ip.run_cell(code)
158
158
159
159
160 indentationerror_file = """if True:
160 indentationerror_file = """if True:
161 zoon()
161 zoom()
162 """
162 """
163
163
164 class IndentationErrorTest(unittest.TestCase):
164 class IndentationErrorTest(unittest.TestCase):
165 def test_indentationerror_shows_line(self):
165 def test_indentationerror_shows_line(self):
166 # See issue gh-2398
166 # See issue gh-2398
167 with tt.AssertPrints("IndentationError"):
167 with tt.AssertPrints("IndentationError"):
168 with tt.AssertPrints("zoon()", suppress=False):
168 with tt.AssertPrints("zoom()", suppress=False):
169 ip.run_cell(indentationerror_file)
169 ip.run_cell(indentationerror_file)
170
170
171 with TemporaryDirectory() as td:
171 with TemporaryDirectory() as td:
172 fname = os.path.join(td, "foo.py")
172 fname = os.path.join(td, "foo.py")
173 with open(fname, "w", encoding="utf-8") as f:
173 with open(fname, "w", encoding="utf-8") as f:
174 f.write(indentationerror_file)
174 f.write(indentationerror_file)
175
175
176 with tt.AssertPrints("IndentationError"):
176 with tt.AssertPrints("IndentationError"):
177 with tt.AssertPrints("zoon()", suppress=False):
177 with tt.AssertPrints("zoom()", suppress=False):
178 ip.magic('run %s' % fname)
178 ip.magic('run %s' % fname)
179
179
180 @skip_without("pandas")
180 @skip_without("pandas")
181 def test_dynamic_code():
181 def test_dynamic_code():
182 code = """
182 code = """
183 import pandas
183 import pandas
184 df = pandas.DataFrame([])
184 df = pandas.DataFrame([])
185
185
186 # Important: only fails inside of an "exec" call:
186 # Important: only fails inside of an "exec" call:
187 exec("df.foobarbaz()")
187 exec("df.foobarbaz()")
188 """
188 """
189
189
190 with tt.AssertPrints("Could not get source"):
190 with tt.AssertPrints("Could not get source"):
191 ip.run_cell(code)
191 ip.run_cell(code)
192
192
193
193
194 se_file_1 = """1
194 se_file_1 = """1
195 2
195 2
196 7/
196 7/
197 """
197 """
198
198
199 se_file_2 = """7/
199 se_file_2 = """7/
200 """
200 """
201
201
202 class SyntaxErrorTest(unittest.TestCase):
202 class SyntaxErrorTest(unittest.TestCase):
203
203
204 def test_syntaxerror_no_stacktrace_at_compile_time(self):
204 def test_syntaxerror_no_stacktrace_at_compile_time(self):
205 syntax_error_at_compile_time = """
205 syntax_error_at_compile_time = """
206 def foo():
206 def foo():
207 ..
207 ..
208 """
208 """
209 with tt.AssertPrints("SyntaxError"):
209 with tt.AssertPrints("SyntaxError"):
210 ip.run_cell(syntax_error_at_compile_time)
210 ip.run_cell(syntax_error_at_compile_time)
211
211
212 with tt.AssertNotPrints("foo()"):
212 with tt.AssertNotPrints("foo()"):
213 ip.run_cell(syntax_error_at_compile_time)
213 ip.run_cell(syntax_error_at_compile_time)
214
214
215 def test_syntaxerror_stacktrace_when_running_compiled_code(self):
215 def test_syntaxerror_stacktrace_when_running_compiled_code(self):
216 syntax_error_at_runtime = """
216 syntax_error_at_runtime = """
217 def foo():
217 def foo():
218 eval("..")
218 eval("..")
219
219
220 def bar():
220 def bar():
221 foo()
221 foo()
222
222
223 bar()
223 bar()
224 """
224 """
225 with tt.AssertPrints("SyntaxError"):
225 with tt.AssertPrints("SyntaxError"):
226 ip.run_cell(syntax_error_at_runtime)
226 ip.run_cell(syntax_error_at_runtime)
227 # Assert syntax error during runtime generate stacktrace
227 # Assert syntax error during runtime generate stacktrace
228 with tt.AssertPrints(["foo()", "bar()"]):
228 with tt.AssertPrints(["foo()", "bar()"]):
229 ip.run_cell(syntax_error_at_runtime)
229 ip.run_cell(syntax_error_at_runtime)
230 del ip.user_ns['bar']
230 del ip.user_ns['bar']
231 del ip.user_ns['foo']
231 del ip.user_ns['foo']
232
232
233 def test_changing_py_file(self):
233 def test_changing_py_file(self):
234 with TemporaryDirectory() as td:
234 with TemporaryDirectory() as td:
235 fname = os.path.join(td, "foo.py")
235 fname = os.path.join(td, "foo.py")
236 with open(fname, "w", encoding="utf-8") as f:
236 with open(fname, "w", encoding="utf-8") as f:
237 f.write(se_file_1)
237 f.write(se_file_1)
238
238
239 with tt.AssertPrints(["7/", "SyntaxError"]):
239 with tt.AssertPrints(["7/", "SyntaxError"]):
240 ip.magic("run " + fname)
240 ip.magic("run " + fname)
241
241
242 # Modify the file
242 # Modify the file
243 with open(fname, "w", encoding="utf-8") as f:
243 with open(fname, "w", encoding="utf-8") as f:
244 f.write(se_file_2)
244 f.write(se_file_2)
245
245
246 # The SyntaxError should point to the correct line
246 # The SyntaxError should point to the correct line
247 with tt.AssertPrints(["7/", "SyntaxError"]):
247 with tt.AssertPrints(["7/", "SyntaxError"]):
248 ip.magic("run " + fname)
248 ip.magic("run " + fname)
249
249
250 def test_non_syntaxerror(self):
250 def test_non_syntaxerror(self):
251 # SyntaxTB may be called with an error other than a SyntaxError
251 # SyntaxTB may be called with an error other than a SyntaxError
252 # See e.g. gh-4361
252 # See e.g. gh-4361
253 try:
253 try:
254 raise ValueError('QWERTY')
254 raise ValueError('QWERTY')
255 except ValueError:
255 except ValueError:
256 with tt.AssertPrints('QWERTY'):
256 with tt.AssertPrints('QWERTY'):
257 ip.showsyntaxerror()
257 ip.showsyntaxerror()
258
258
259 import sys
259 import sys
260
260
261 if platform.python_implementation() != "PyPy":
261 if platform.python_implementation() != "PyPy":
262 """
262 """
263 New 3.9 Pgen Parser does not raise Memory error, except on failed malloc.
263 New 3.9 Pgen Parser does not raise Memory error, except on failed malloc.
264 """
264 """
265 class MemoryErrorTest(unittest.TestCase):
265 class MemoryErrorTest(unittest.TestCase):
266 def test_memoryerror(self):
266 def test_memoryerror(self):
267 memoryerror_code = "(" * 200 + ")" * 200
267 memoryerror_code = "(" * 200 + ")" * 200
268 ip.run_cell(memoryerror_code)
268 ip.run_cell(memoryerror_code)
269
269
270
270
271 class Python3ChainedExceptionsTest(unittest.TestCase):
271 class Python3ChainedExceptionsTest(unittest.TestCase):
272 DIRECT_CAUSE_ERROR_CODE = """
272 DIRECT_CAUSE_ERROR_CODE = """
273 try:
273 try:
274 x = 1 + 2
274 x = 1 + 2
275 print(not_defined_here)
275 print(not_defined_here)
276 except Exception as e:
276 except Exception as e:
277 x += 55
277 x += 55
278 x - 1
278 x - 1
279 y = {}
279 y = {}
280 raise KeyError('uh') from e
280 raise KeyError('uh') from e
281 """
281 """
282
282
283 EXCEPTION_DURING_HANDLING_CODE = """
283 EXCEPTION_DURING_HANDLING_CODE = """
284 try:
284 try:
285 x = 1 + 2
285 x = 1 + 2
286 print(not_defined_here)
286 print(not_defined_here)
287 except Exception as e:
287 except Exception as e:
288 x += 55
288 x += 55
289 x - 1
289 x - 1
290 y = {}
290 y = {}
291 raise KeyError('uh')
291 raise KeyError('uh')
292 """
292 """
293
293
294 SUPPRESS_CHAINING_CODE = """
294 SUPPRESS_CHAINING_CODE = """
295 try:
295 try:
296 1/0
296 1/0
297 except Exception:
297 except Exception:
298 raise ValueError("Yikes") from None
298 raise ValueError("Yikes") from None
299 """
299 """
300
300
301 SYS_EXIT_WITH_CONTEXT_CODE = """
301 SYS_EXIT_WITH_CONTEXT_CODE = """
302 try:
302 try:
303 1/0
303 1/0
304 except Exception as e:
304 except Exception as e:
305 raise SystemExit(1)
305 raise SystemExit(1)
306 """
306 """
307
307
308 def test_direct_cause_error(self):
308 def test_direct_cause_error(self):
309 with tt.AssertPrints(["KeyError", "NameError", "direct cause"]):
309 with tt.AssertPrints(["KeyError", "NameError", "direct cause"]):
310 ip.run_cell(self.DIRECT_CAUSE_ERROR_CODE)
310 ip.run_cell(self.DIRECT_CAUSE_ERROR_CODE)
311
311
312 def test_exception_during_handling_error(self):
312 def test_exception_during_handling_error(self):
313 with tt.AssertPrints(["KeyError", "NameError", "During handling"]):
313 with tt.AssertPrints(["KeyError", "NameError", "During handling"]):
314 ip.run_cell(self.EXCEPTION_DURING_HANDLING_CODE)
314 ip.run_cell(self.EXCEPTION_DURING_HANDLING_CODE)
315
315
316 def test_sysexit_while_handling_error(self):
316 def test_sysexit_while_handling_error(self):
317 with tt.AssertPrints(["SystemExit", "to see the full traceback"]):
317 with tt.AssertPrints(["SystemExit", "to see the full traceback"]):
318 with tt.AssertNotPrints(["another exception"], suppress=False):
318 with tt.AssertNotPrints(["another exception"], suppress=False):
319 ip.run_cell(self.SYS_EXIT_WITH_CONTEXT_CODE)
319 ip.run_cell(self.SYS_EXIT_WITH_CONTEXT_CODE)
320
320
321 def test_suppress_exception_chaining(self):
321 def test_suppress_exception_chaining(self):
322 with tt.AssertNotPrints("ZeroDivisionError"), \
322 with tt.AssertNotPrints("ZeroDivisionError"), \
323 tt.AssertPrints("ValueError", suppress=False):
323 tt.AssertPrints("ValueError", suppress=False):
324 ip.run_cell(self.SUPPRESS_CHAINING_CODE)
324 ip.run_cell(self.SUPPRESS_CHAINING_CODE)
325
325
326 def test_plain_direct_cause_error(self):
326 def test_plain_direct_cause_error(self):
327 with tt.AssertPrints(["KeyError", "NameError", "direct cause"]):
327 with tt.AssertPrints(["KeyError", "NameError", "direct cause"]):
328 ip.run_cell("%xmode Plain")
328 ip.run_cell("%xmode Plain")
329 ip.run_cell(self.DIRECT_CAUSE_ERROR_CODE)
329 ip.run_cell(self.DIRECT_CAUSE_ERROR_CODE)
330 ip.run_cell("%xmode Verbose")
330 ip.run_cell("%xmode Verbose")
331
331
332 def test_plain_exception_during_handling_error(self):
332 def test_plain_exception_during_handling_error(self):
333 with tt.AssertPrints(["KeyError", "NameError", "During handling"]):
333 with tt.AssertPrints(["KeyError", "NameError", "During handling"]):
334 ip.run_cell("%xmode Plain")
334 ip.run_cell("%xmode Plain")
335 ip.run_cell(self.EXCEPTION_DURING_HANDLING_CODE)
335 ip.run_cell(self.EXCEPTION_DURING_HANDLING_CODE)
336 ip.run_cell("%xmode Verbose")
336 ip.run_cell("%xmode Verbose")
337
337
338 def test_plain_suppress_exception_chaining(self):
338 def test_plain_suppress_exception_chaining(self):
339 with tt.AssertNotPrints("ZeroDivisionError"), \
339 with tt.AssertNotPrints("ZeroDivisionError"), \
340 tt.AssertPrints("ValueError", suppress=False):
340 tt.AssertPrints("ValueError", suppress=False):
341 ip.run_cell("%xmode Plain")
341 ip.run_cell("%xmode Plain")
342 ip.run_cell(self.SUPPRESS_CHAINING_CODE)
342 ip.run_cell(self.SUPPRESS_CHAINING_CODE)
343 ip.run_cell("%xmode Verbose")
343 ip.run_cell("%xmode Verbose")
344
344
345
345
346 class RecursionTest(unittest.TestCase):
346 class RecursionTest(unittest.TestCase):
347 DEFINITIONS = """
347 DEFINITIONS = """
348 def non_recurs():
348 def non_recurs():
349 1/0
349 1/0
350
350
351 def r1():
351 def r1():
352 r1()
352 r1()
353
353
354 def r3a():
354 def r3a():
355 r3b()
355 r3b()
356
356
357 def r3b():
357 def r3b():
358 r3c()
358 r3c()
359
359
360 def r3c():
360 def r3c():
361 r3a()
361 r3a()
362
362
363 def r3o1():
363 def r3o1():
364 r3a()
364 r3a()
365
365
366 def r3o2():
366 def r3o2():
367 r3o1()
367 r3o1()
368 """
368 """
369 def setUp(self):
369 def setUp(self):
370 ip.run_cell(self.DEFINITIONS)
370 ip.run_cell(self.DEFINITIONS)
371
371
372 def test_no_recursion(self):
372 def test_no_recursion(self):
373 with tt.AssertNotPrints("skipping similar frames"):
373 with tt.AssertNotPrints("skipping similar frames"):
374 ip.run_cell("non_recurs()")
374 ip.run_cell("non_recurs()")
375
375
376 @recursionlimit(200)
376 @recursionlimit(200)
377 def test_recursion_one_frame(self):
377 def test_recursion_one_frame(self):
378 with tt.AssertPrints(re.compile(
378 with tt.AssertPrints(re.compile(
379 r"\[\.\.\. skipping similar frames: r1 at line 5 \(\d{2,3} times\)\]")
379 r"\[\.\.\. skipping similar frames: r1 at line 5 \(\d{2,3} times\)\]")
380 ):
380 ):
381 ip.run_cell("r1()")
381 ip.run_cell("r1()")
382
382
383 @recursionlimit(160)
383 @recursionlimit(160)
384 def test_recursion_three_frames(self):
384 def test_recursion_three_frames(self):
385 with tt.AssertPrints("[... skipping similar frames: "), \
385 with tt.AssertPrints("[... skipping similar frames: "), \
386 tt.AssertPrints(re.compile(r"r3a at line 8 \(\d{2} times\)"), suppress=False), \
386 tt.AssertPrints(re.compile(r"r3a at line 8 \(\d{2} times\)"), suppress=False), \
387 tt.AssertPrints(re.compile(r"r3b at line 11 \(\d{2} times\)"), suppress=False), \
387 tt.AssertPrints(re.compile(r"r3b at line 11 \(\d{2} times\)"), suppress=False), \
388 tt.AssertPrints(re.compile(r"r3c at line 14 \(\d{2} times\)"), suppress=False):
388 tt.AssertPrints(re.compile(r"r3c at line 14 \(\d{2} times\)"), suppress=False):
389 ip.run_cell("r3o2()")
389 ip.run_cell("r3o2()")
390
390
391
391
392 class PEP678NotesReportingTest(unittest.TestCase):
392 class PEP678NotesReportingTest(unittest.TestCase):
393 ERROR_WITH_NOTE = """
393 ERROR_WITH_NOTE = """
394 try:
394 try:
395 raise AssertionError("Message")
395 raise AssertionError("Message")
396 except Exception as e:
396 except Exception as e:
397 try:
397 try:
398 e.add_note("This is a PEP-678 note.")
398 e.add_note("This is a PEP-678 note.")
399 except AttributeError: # Python <= 3.10
399 except AttributeError: # Python <= 3.10
400 e.__notes__ = ("This is a PEP-678 note.",)
400 e.__notes__ = ("This is a PEP-678 note.",)
401 raise
401 raise
402 """
402 """
403
403
404 def test_verbose_reports_notes(self):
404 def test_verbose_reports_notes(self):
405 with tt.AssertPrints(["AssertionError", "Message", "This is a PEP-678 note."]):
405 with tt.AssertPrints(["AssertionError", "Message", "This is a PEP-678 note."]):
406 ip.run_cell(self.ERROR_WITH_NOTE)
406 ip.run_cell(self.ERROR_WITH_NOTE)
407
407
408 def test_plain_reports_notes(self):
408 def test_plain_reports_notes(self):
409 with tt.AssertPrints(["AssertionError", "Message", "This is a PEP-678 note."]):
409 with tt.AssertPrints(["AssertionError", "Message", "This is a PEP-678 note."]):
410 ip.run_cell("%xmode Plain")
410 ip.run_cell("%xmode Plain")
411 ip.run_cell(self.ERROR_WITH_NOTE)
411 ip.run_cell(self.ERROR_WITH_NOTE)
412 ip.run_cell("%xmode Verbose")
412 ip.run_cell("%xmode Verbose")
413
413
414
414
415 #----------------------------------------------------------------------------
415 #----------------------------------------------------------------------------
416
416
417 # module testing (minimal)
417 # module testing (minimal)
418 def test_handlers():
418 def test_handlers():
419 def spam(c, d_e):
419 def spam(c, d_e):
420 (d, e) = d_e
420 (d, e) = d_e
421 x = c + d
421 x = c + d
422 y = c * d
422 y = c * d
423 foo(x, y)
423 foo(x, y)
424
424
425 def foo(a, b, bar=1):
425 def foo(a, b, bar=1):
426 eggs(a, b + bar)
426 eggs(a, b + bar)
427
427
428 def eggs(f, g, z=globals()):
428 def eggs(f, g, z=globals()):
429 h = f + g
429 h = f + g
430 i = f - g
430 i = f - g
431 return h / i
431 return h / i
432
432
433 buff = io.StringIO()
433 buff = io.StringIO()
434
434
435 buff.write('')
435 buff.write('')
436 buff.write('*** Before ***')
436 buff.write('*** Before ***')
437 try:
437 try:
438 buff.write(spam(1, (2, 3)))
438 buff.write(spam(1, (2, 3)))
439 except:
439 except:
440 traceback.print_exc(file=buff)
440 traceback.print_exc(file=buff)
441
441
442 handler = ColorTB(ostream=buff)
442 handler = ColorTB(ostream=buff)
443 buff.write('*** ColorTB ***')
443 buff.write('*** ColorTB ***')
444 try:
444 try:
445 buff.write(spam(1, (2, 3)))
445 buff.write(spam(1, (2, 3)))
446 except:
446 except:
447 handler(*sys.exc_info())
447 handler(*sys.exc_info())
448 buff.write('')
448 buff.write('')
449
449
450 handler = VerboseTB(ostream=buff)
450 handler = VerboseTB(ostream=buff)
451 buff.write('*** VerboseTB ***')
451 buff.write('*** VerboseTB ***')
452 try:
452 try:
453 buff.write(spam(1, (2, 3)))
453 buff.write(spam(1, (2, 3)))
454 except:
454 except:
455 handler(*sys.exc_info())
455 handler(*sys.exc_info())
456 buff.write('')
456 buff.write('')
General Comments 0
You need to be logged in to leave comments. Login now