##// END OF EJS Templates
MAINT: mark 3.13.dev failing test as xfail (#14468)
M Bussonnier -
r28802:4cb90241 merge
parent child Browse files
Show More
@@ -1,1749 +1,1759
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
14
15
13 from contextlib import contextmanager
16 from contextlib import contextmanager
14
17
15 from traitlets.config.loader import Config
18 from traitlets.config.loader import Config
16 from IPython import get_ipython
19 from IPython import get_ipython
17 from IPython.core import completer
20 from IPython.core import completer
18 from IPython.utils.tempdir import TemporaryDirectory, TemporaryWorkingDirectory
21 from IPython.utils.tempdir import TemporaryDirectory, TemporaryWorkingDirectory
19 from IPython.utils.generics import complete_object
22 from IPython.utils.generics import complete_object
20 from IPython.testing import decorators as dec
23 from IPython.testing import decorators as dec
21
24
22 from IPython.core.completer import (
25 from IPython.core.completer import (
23 Completion,
26 Completion,
24 provisionalcompleter,
27 provisionalcompleter,
25 match_dict_keys,
28 match_dict_keys,
26 _deduplicate_completions,
29 _deduplicate_completions,
27 _match_number_in_dict_key_prefix,
30 _match_number_in_dict_key_prefix,
28 completion_matcher,
31 completion_matcher,
29 SimpleCompletion,
32 SimpleCompletion,
30 CompletionContext,
33 CompletionContext,
31 )
34 )
32
35
36 from packaging.version import parse
37
38
33 # -----------------------------------------------------------------------------
39 # -----------------------------------------------------------------------------
34 # Test functions
40 # Test functions
35 # -----------------------------------------------------------------------------
41 # -----------------------------------------------------------------------------
36
42
37
43
38 def recompute_unicode_ranges():
44 def recompute_unicode_ranges():
39 """
45 """
40 utility to recompute the largest unicode range without any characters
46 utility to recompute the largest unicode range without any characters
41
47
42 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
43 """
49 """
44 import itertools
50 import itertools
45 import unicodedata
51 import unicodedata
46
52
47 valid = []
53 valid = []
48 for c in range(0, 0x10FFFF + 1):
54 for c in range(0, 0x10FFFF + 1):
49 try:
55 try:
50 unicodedata.name(chr(c))
56 unicodedata.name(chr(c))
51 except ValueError:
57 except ValueError:
52 continue
58 continue
53 valid.append(c)
59 valid.append(c)
54
60
55 def ranges(i):
61 def ranges(i):
56 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]):
57 b = list(b)
63 b = list(b)
58 yield b[0][1], b[-1][1]
64 yield b[0][1], b[-1][1]
59
65
60 rg = list(ranges(valid))
66 rg = list(ranges(valid))
61 lens = []
67 lens = []
62 gap_lens = []
68 gap_lens = []
63 pstart, pstop = 0, 0
69 pstart, pstop = 0, 0
64 for start, stop in rg:
70 for start, stop in rg:
65 lens.append(stop - start)
71 lens.append(stop - start)
66 gap_lens.append(
72 gap_lens.append(
67 (
73 (
68 start - pstop,
74 start - pstop,
69 hex(pstop + 1),
75 hex(pstop + 1),
70 hex(start),
76 hex(start),
71 f"{round((start - pstop)/0xe01f0*100)}%",
77 f"{round((start - pstop)/0xe01f0*100)}%",
72 )
78 )
73 )
79 )
74 pstart, pstop = start, stop
80 pstart, pstop = start, stop
75
81
76 return sorted(gap_lens)[-1]
82 return sorted(gap_lens)[-1]
77
83
78
84
79 def test_unicode_range():
85 def test_unicode_range():
80 """
86 """
81 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
82 results than testing the full length.
88 results than testing the full length.
83 """
89 """
84 from IPython.core.completer import _unicode_name_compute, _UNICODE_RANGES
90 from IPython.core.completer import _unicode_name_compute, _UNICODE_RANGES
85
91
86 expected_list = _unicode_name_compute([(0, 0x110000)])
92 expected_list = _unicode_name_compute([(0, 0x110000)])
87 test = _unicode_name_compute(_UNICODE_RANGES)
93 test = _unicode_name_compute(_UNICODE_RANGES)
88 len_exp = len(expected_list)
94 len_exp = len(expected_list)
89 len_test = len(test)
95 len_test = len(test)
90
96
91 # 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 +
92 # elements.
98 # elements.
93 message = None
99 message = None
94 if len_exp != len_test or len_exp > 131808:
100 if len_exp != len_test or len_exp > 131808:
95 size, start, stop, prct = recompute_unicode_ranges()
101 size, start, stop, prct = recompute_unicode_ranges()
96 message = f"""_UNICODE_RANGES likely wrong and need updating. This is
102 message = f"""_UNICODE_RANGES likely wrong and need updating. This is
97 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
98 in unicode characters has reduces in size to be {size} characters
104 in unicode characters has reduces in size to be {size} characters
99 ({prct}), from {start}, to {stop}. In completer.py likely update to
105 ({prct}), from {start}, to {stop}. In completer.py likely update to
100
106
101 _UNICODE_RANGES = [(32, {start}), ({stop}, 0xe01f0)]
107 _UNICODE_RANGES = [(32, {start}), ({stop}, 0xe01f0)]
102
108
103 And update the assertion below to use
109 And update the assertion below to use
104
110
105 len_exp <= {len_exp}
111 len_exp <= {len_exp}
106 """
112 """
107 assert len_exp == len_test, message
113 assert len_exp == len_test, message
108
114
109 # fail if new unicode symbols have been added.
115 # fail if new unicode symbols have been added.
110 assert len_exp <= 143668, message
116 assert len_exp <= 143668, message
111
117
112
118
113 @contextmanager
119 @contextmanager
114 def greedy_completion():
120 def greedy_completion():
115 ip = get_ipython()
121 ip = get_ipython()
116 greedy_original = ip.Completer.greedy
122 greedy_original = ip.Completer.greedy
117 try:
123 try:
118 ip.Completer.greedy = True
124 ip.Completer.greedy = True
119 yield
125 yield
120 finally:
126 finally:
121 ip.Completer.greedy = greedy_original
127 ip.Completer.greedy = greedy_original
122
128
123
129
124 @contextmanager
130 @contextmanager
125 def evaluation_policy(evaluation: str):
131 def evaluation_policy(evaluation: str):
126 ip = get_ipython()
132 ip = get_ipython()
127 evaluation_original = ip.Completer.evaluation
133 evaluation_original = ip.Completer.evaluation
128 try:
134 try:
129 ip.Completer.evaluation = evaluation
135 ip.Completer.evaluation = evaluation
130 yield
136 yield
131 finally:
137 finally:
132 ip.Completer.evaluation = evaluation_original
138 ip.Completer.evaluation = evaluation_original
133
139
134
140
135 @contextmanager
141 @contextmanager
136 def custom_matchers(matchers):
142 def custom_matchers(matchers):
137 ip = get_ipython()
143 ip = get_ipython()
138 try:
144 try:
139 ip.Completer.custom_matchers.extend(matchers)
145 ip.Completer.custom_matchers.extend(matchers)
140 yield
146 yield
141 finally:
147 finally:
142 ip.Completer.custom_matchers.clear()
148 ip.Completer.custom_matchers.clear()
143
149
144
150
145 def test_protect_filename():
151 def test_protect_filename():
146 if sys.platform == "win32":
152 if sys.platform == "win32":
147 pairs = [
153 pairs = [
148 ("abc", "abc"),
154 ("abc", "abc"),
149 (" abc", '" abc"'),
155 (" abc", '" abc"'),
150 ("a bc", '"a bc"'),
156 ("a bc", '"a bc"'),
151 ("a bc", '"a bc"'),
157 ("a bc", '"a bc"'),
152 (" bc", '" bc"'),
158 (" bc", '" bc"'),
153 ]
159 ]
154 else:
160 else:
155 pairs = [
161 pairs = [
156 ("abc", "abc"),
162 ("abc", "abc"),
157 (" abc", r"\ abc"),
163 (" abc", r"\ abc"),
158 ("a bc", r"a\ bc"),
164 ("a bc", r"a\ bc"),
159 ("a bc", r"a\ \ bc"),
165 ("a bc", r"a\ \ bc"),
160 (" bc", r"\ \ bc"),
166 (" bc", r"\ \ bc"),
161 # On posix, we also protect parens and other special characters.
167 # On posix, we also protect parens and other special characters.
162 ("a(bc", r"a\(bc"),
168 ("a(bc", r"a\(bc"),
163 ("a)bc", r"a\)bc"),
169 ("a)bc", r"a\)bc"),
164 ("a( )bc", r"a\(\ \)bc"),
170 ("a( )bc", r"a\(\ \)bc"),
165 ("a[1]bc", r"a\[1\]bc"),
171 ("a[1]bc", r"a\[1\]bc"),
166 ("a{1}bc", r"a\{1\}bc"),
172 ("a{1}bc", r"a\{1\}bc"),
167 ("a#bc", r"a\#bc"),
173 ("a#bc", r"a\#bc"),
168 ("a?bc", r"a\?bc"),
174 ("a?bc", r"a\?bc"),
169 ("a=bc", r"a\=bc"),
175 ("a=bc", r"a\=bc"),
170 ("a\\bc", r"a\\bc"),
176 ("a\\bc", r"a\\bc"),
171 ("a|bc", r"a\|bc"),
177 ("a|bc", r"a\|bc"),
172 ("a;bc", r"a\;bc"),
178 ("a;bc", r"a\;bc"),
173 ("a:bc", r"a\:bc"),
179 ("a:bc", r"a\:bc"),
174 ("a'bc", r"a\'bc"),
180 ("a'bc", r"a\'bc"),
175 ("a*bc", r"a\*bc"),
181 ("a*bc", r"a\*bc"),
176 ('a"bc', r"a\"bc"),
182 ('a"bc', r"a\"bc"),
177 ("a^bc", r"a\^bc"),
183 ("a^bc", r"a\^bc"),
178 ("a&bc", r"a\&bc"),
184 ("a&bc", r"a\&bc"),
179 ]
185 ]
180 # run the actual tests
186 # run the actual tests
181 for s1, s2 in pairs:
187 for s1, s2 in pairs:
182 s1p = completer.protect_filename(s1)
188 s1p = completer.protect_filename(s1)
183 assert s1p == s2
189 assert s1p == s2
184
190
185
191
186 def check_line_split(splitter, test_specs):
192 def check_line_split(splitter, test_specs):
187 for part1, part2, split in test_specs:
193 for part1, part2, split in test_specs:
188 cursor_pos = len(part1)
194 cursor_pos = len(part1)
189 line = part1 + part2
195 line = part1 + part2
190 out = splitter.split_line(line, cursor_pos)
196 out = splitter.split_line(line, cursor_pos)
191 assert out == split
197 assert out == split
192
198
193 def test_line_split():
199 def test_line_split():
194 """Basic line splitter test with default specs."""
200 """Basic line splitter test with default specs."""
195 sp = completer.CompletionSplitter()
201 sp = completer.CompletionSplitter()
196 # 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
197 # 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
198 # 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
199 # tab at the end of the line, the most common case.
205 # tab at the end of the line, the most common case.
200 t = [
206 t = [
201 ("run some/scrip", "", "some/scrip"),
207 ("run some/scrip", "", "some/scrip"),
202 ("run scripts/er", "ror.py foo", "scripts/er"),
208 ("run scripts/er", "ror.py foo", "scripts/er"),
203 ("echo $HOM", "", "HOM"),
209 ("echo $HOM", "", "HOM"),
204 ("print sys.pa", "", "sys.pa"),
210 ("print sys.pa", "", "sys.pa"),
205 ("print(sys.pa", "", "sys.pa"),
211 ("print(sys.pa", "", "sys.pa"),
206 ("execfile('scripts/er", "", "scripts/er"),
212 ("execfile('scripts/er", "", "scripts/er"),
207 ("a[x.", "", "x."),
213 ("a[x.", "", "x."),
208 ("a[x.", "y", "x."),
214 ("a[x.", "y", "x."),
209 ('cd "some_file/', "", "some_file/"),
215 ('cd "some_file/', "", "some_file/"),
210 ]
216 ]
211 check_line_split(sp, t)
217 check_line_split(sp, t)
212 # 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
213 # all inputs turned into unicode
219 # all inputs turned into unicode
214 check_line_split(sp, [map(str, p) for p in t])
220 check_line_split(sp, [map(str, p) for p in t])
215
221
216
222
217 class NamedInstanceClass:
223 class NamedInstanceClass:
218 instances = {}
224 instances = {}
219
225
220 def __init__(self, name):
226 def __init__(self, name):
221 self.instances[name] = self
227 self.instances[name] = self
222
228
223 @classmethod
229 @classmethod
224 def _ipython_key_completions_(cls):
230 def _ipython_key_completions_(cls):
225 return cls.instances.keys()
231 return cls.instances.keys()
226
232
227
233
228 class KeyCompletable:
234 class KeyCompletable:
229 def __init__(self, things=()):
235 def __init__(self, things=()):
230 self.things = things
236 self.things = things
231
237
232 def _ipython_key_completions_(self):
238 def _ipython_key_completions_(self):
233 return list(self.things)
239 return list(self.things)
234
240
235
241
236 class TestCompleter(unittest.TestCase):
242 class TestCompleter(unittest.TestCase):
237 def setUp(self):
243 def setUp(self):
238 """
244 """
239 We want to silence all PendingDeprecationWarning when testing the completer
245 We want to silence all PendingDeprecationWarning when testing the completer
240 """
246 """
241 self._assertwarns = self.assertWarns(PendingDeprecationWarning)
247 self._assertwarns = self.assertWarns(PendingDeprecationWarning)
242 self._assertwarns.__enter__()
248 self._assertwarns.__enter__()
243
249
244 def tearDown(self):
250 def tearDown(self):
245 try:
251 try:
246 self._assertwarns.__exit__(None, None, None)
252 self._assertwarns.__exit__(None, None, None)
247 except AssertionError:
253 except AssertionError:
248 pass
254 pass
249
255
250 def test_custom_completion_error(self):
256 def test_custom_completion_error(self):
251 """Test that errors from custom attribute completers are silenced."""
257 """Test that errors from custom attribute completers are silenced."""
252 ip = get_ipython()
258 ip = get_ipython()
253
259
254 class A:
260 class A:
255 pass
261 pass
256
262
257 ip.user_ns["x"] = A()
263 ip.user_ns["x"] = A()
258
264
259 @complete_object.register(A)
265 @complete_object.register(A)
260 def complete_A(a, existing_completions):
266 def complete_A(a, existing_completions):
261 raise TypeError("this should be silenced")
267 raise TypeError("this should be silenced")
262
268
263 ip.complete("x.")
269 ip.complete("x.")
264
270
265 def test_custom_completion_ordering(self):
271 def test_custom_completion_ordering(self):
266 """Test that errors from custom attribute completers are silenced."""
272 """Test that errors from custom attribute completers are silenced."""
267 ip = get_ipython()
273 ip = get_ipython()
268
274
269 _, matches = ip.complete('in')
275 _, matches = ip.complete('in')
270 assert matches.index('input') < matches.index('int')
276 assert matches.index('input') < matches.index('int')
271
277
272 def complete_example(a):
278 def complete_example(a):
273 return ['example2', 'example1']
279 return ['example2', 'example1']
274
280
275 ip.Completer.custom_completers.add_re('ex*', complete_example)
281 ip.Completer.custom_completers.add_re('ex*', complete_example)
276 _, matches = ip.complete('ex')
282 _, matches = ip.complete('ex')
277 assert matches.index('example2') < matches.index('example1')
283 assert matches.index('example2') < matches.index('example1')
278
284
279 def test_unicode_completions(self):
285 def test_unicode_completions(self):
280 ip = get_ipython()
286 ip = get_ipython()
281 # Some strings that trigger different types of completion. Check them both
287 # Some strings that trigger different types of completion. Check them both
282 # in str and unicode forms
288 # in str and unicode forms
283 s = ["ru", "%ru", "cd /", "floa", "float(x)/"]
289 s = ["ru", "%ru", "cd /", "floa", "float(x)/"]
284 for t in s + list(map(str, s)):
290 for t in s + list(map(str, s)):
285 # 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
286 # 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
287 # 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
288 # values.
294 # values.
289 text, matches = ip.complete(t)
295 text, matches = ip.complete(t)
290 self.assertIsInstance(text, str)
296 self.assertIsInstance(text, str)
291 self.assertIsInstance(matches, list)
297 self.assertIsInstance(matches, list)
292
298
293 def test_latex_completions(self):
299 def test_latex_completions(self):
294 from IPython.core.latex_symbols import latex_symbols
300 from IPython.core.latex_symbols import latex_symbols
295 import random
301 import random
296
302
297 ip = get_ipython()
303 ip = get_ipython()
298 # Test some random unicode symbols
304 # Test some random unicode symbols
299 keys = random.sample(sorted(latex_symbols), 10)
305 keys = random.sample(sorted(latex_symbols), 10)
300 for k in keys:
306 for k in keys:
301 text, matches = ip.complete(k)
307 text, matches = ip.complete(k)
302 self.assertEqual(text, k)
308 self.assertEqual(text, k)
303 self.assertEqual(matches, [latex_symbols[k]])
309 self.assertEqual(matches, [latex_symbols[k]])
304 # Test a more complex line
310 # Test a more complex line
305 text, matches = ip.complete("print(\\alpha")
311 text, matches = ip.complete("print(\\alpha")
306 self.assertEqual(text, "\\alpha")
312 self.assertEqual(text, "\\alpha")
307 self.assertEqual(matches[0], latex_symbols["\\alpha"])
313 self.assertEqual(matches[0], latex_symbols["\\alpha"])
308 # Test multiple matching latex symbols
314 # Test multiple matching latex symbols
309 text, matches = ip.complete("\\al")
315 text, matches = ip.complete("\\al")
310 self.assertIn("\\alpha", matches)
316 self.assertIn("\\alpha", matches)
311 self.assertIn("\\aleph", matches)
317 self.assertIn("\\aleph", matches)
312
318
313 def test_latex_no_results(self):
319 def test_latex_no_results(self):
314 """
320 """
315 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.
316 """
322 """
317 ip = get_ipython()
323 ip = get_ipython()
318 text, matches = ip.Completer.latex_matches("\\really_i_should_match_nothing")
324 text, matches = ip.Completer.latex_matches("\\really_i_should_match_nothing")
319 self.assertEqual(text, "")
325 self.assertEqual(text, "")
320 self.assertEqual(matches, ())
326 self.assertEqual(matches, ())
321
327
322 def test_back_latex_completion(self):
328 def test_back_latex_completion(self):
323 ip = get_ipython()
329 ip = get_ipython()
324
330
325 # 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.
326 name, matches = ip.complete("\\Ξ²")
332 name, matches = ip.complete("\\Ξ²")
327 self.assertEqual(matches, ["\\beta"])
333 self.assertEqual(matches, ["\\beta"])
328
334
329 def test_back_unicode_completion(self):
335 def test_back_unicode_completion(self):
330 ip = get_ipython()
336 ip = get_ipython()
331
337
332 name, matches = ip.complete("\\β…€")
338 name, matches = ip.complete("\\β…€")
333 self.assertEqual(matches, ["\\ROMAN NUMERAL FIVE"])
339 self.assertEqual(matches, ["\\ROMAN NUMERAL FIVE"])
334
340
335 def test_forward_unicode_completion(self):
341 def test_forward_unicode_completion(self):
336 ip = get_ipython()
342 ip = get_ipython()
337
343
338 name, matches = ip.complete("\\ROMAN NUMERAL FIVE")
344 name, matches = ip.complete("\\ROMAN NUMERAL FIVE")
339 self.assertEqual(matches, ["β…€"]) # This is not a V
345 self.assertEqual(matches, ["β…€"]) # This is not a V
340 self.assertEqual(matches, ["\u2164"]) # same as above but explicit.
346 self.assertEqual(matches, ["\u2164"]) # same as above but explicit.
341
347
342 def test_delim_setting(self):
348 def test_delim_setting(self):
343 sp = completer.CompletionSplitter()
349 sp = completer.CompletionSplitter()
344 sp.delims = " "
350 sp.delims = " "
345 self.assertEqual(sp.delims, " ")
351 self.assertEqual(sp.delims, " ")
346 self.assertEqual(sp._delim_expr, r"[\ ]")
352 self.assertEqual(sp._delim_expr, r"[\ ]")
347
353
348 def test_spaces(self):
354 def test_spaces(self):
349 """Test with only spaces as split chars."""
355 """Test with only spaces as split chars."""
350 sp = completer.CompletionSplitter()
356 sp = completer.CompletionSplitter()
351 sp.delims = " "
357 sp.delims = " "
352 t = [("foo", "", "foo"), ("run foo", "", "foo"), ("run foo", "bar", "foo")]
358 t = [("foo", "", "foo"), ("run foo", "", "foo"), ("run foo", "bar", "foo")]
353 check_line_split(sp, t)
359 check_line_split(sp, t)
354
360
355 def test_has_open_quotes1(self):
361 def test_has_open_quotes1(self):
356 for s in ["'", "'''", "'hi' '"]:
362 for s in ["'", "'''", "'hi' '"]:
357 self.assertEqual(completer.has_open_quotes(s), "'")
363 self.assertEqual(completer.has_open_quotes(s), "'")
358
364
359 def test_has_open_quotes2(self):
365 def test_has_open_quotes2(self):
360 for s in ['"', '"""', '"hi" "']:
366 for s in ['"', '"""', '"hi" "']:
361 self.assertEqual(completer.has_open_quotes(s), '"')
367 self.assertEqual(completer.has_open_quotes(s), '"')
362
368
363 def test_has_open_quotes3(self):
369 def test_has_open_quotes3(self):
364 for s in ["''", "''' '''", "'hi' 'ipython'"]:
370 for s in ["''", "''' '''", "'hi' 'ipython'"]:
365 self.assertFalse(completer.has_open_quotes(s))
371 self.assertFalse(completer.has_open_quotes(s))
366
372
367 def test_has_open_quotes4(self):
373 def test_has_open_quotes4(self):
368 for s in ['""', '""" """', '"hi" "ipython"']:
374 for s in ['""', '""" """', '"hi" "ipython"']:
369 self.assertFalse(completer.has_open_quotes(s))
375 self.assertFalse(completer.has_open_quotes(s))
370
376
371 @pytest.mark.xfail(
377 @pytest.mark.xfail(
372 sys.platform == "win32", reason="abspath completions fail on Windows"
378 sys.platform == "win32", reason="abspath completions fail on Windows"
373 )
379 )
374 def test_abspath_file_completions(self):
380 def test_abspath_file_completions(self):
375 ip = get_ipython()
381 ip = get_ipython()
376 with TemporaryDirectory() as tmpdir:
382 with TemporaryDirectory() as tmpdir:
377 prefix = os.path.join(tmpdir, "foo")
383 prefix = os.path.join(tmpdir, "foo")
378 suffixes = ["1", "2"]
384 suffixes = ["1", "2"]
379 names = [prefix + s for s in suffixes]
385 names = [prefix + s for s in suffixes]
380 for n in names:
386 for n in names:
381 open(n, "w", encoding="utf-8").close()
387 open(n, "w", encoding="utf-8").close()
382
388
383 # Check simple completion
389 # Check simple completion
384 c = ip.complete(prefix)[1]
390 c = ip.complete(prefix)[1]
385 self.assertEqual(c, names)
391 self.assertEqual(c, names)
386
392
387 # Now check with a function call
393 # Now check with a function call
388 cmd = 'a = f("%s' % prefix
394 cmd = 'a = f("%s' % prefix
389 c = ip.complete(prefix, cmd)[1]
395 c = ip.complete(prefix, cmd)[1]
390 comp = [prefix + s for s in suffixes]
396 comp = [prefix + s for s in suffixes]
391 self.assertEqual(c, comp)
397 self.assertEqual(c, comp)
392
398
393 def test_local_file_completions(self):
399 def test_local_file_completions(self):
394 ip = get_ipython()
400 ip = get_ipython()
395 with TemporaryWorkingDirectory():
401 with TemporaryWorkingDirectory():
396 prefix = "./foo"
402 prefix = "./foo"
397 suffixes = ["1", "2"]
403 suffixes = ["1", "2"]
398 names = [prefix + s for s in suffixes]
404 names = [prefix + s for s in suffixes]
399 for n in names:
405 for n in names:
400 open(n, "w", encoding="utf-8").close()
406 open(n, "w", encoding="utf-8").close()
401
407
402 # Check simple completion
408 # Check simple completion
403 c = ip.complete(prefix)[1]
409 c = ip.complete(prefix)[1]
404 self.assertEqual(c, names)
410 self.assertEqual(c, names)
405
411
406 # Now check with a function call
412 # Now check with a function call
407 cmd = 'a = f("%s' % prefix
413 cmd = 'a = f("%s' % prefix
408 c = ip.complete(prefix, cmd)[1]
414 c = ip.complete(prefix, cmd)[1]
409 comp = {prefix + s for s in suffixes}
415 comp = {prefix + s for s in suffixes}
410 self.assertTrue(comp.issubset(set(c)))
416 self.assertTrue(comp.issubset(set(c)))
411
417
412 def test_quoted_file_completions(self):
418 def test_quoted_file_completions(self):
413 ip = get_ipython()
419 ip = get_ipython()
414
420
415 def _(text):
421 def _(text):
416 return ip.Completer._complete(
422 return ip.Completer._complete(
417 cursor_line=0, cursor_pos=len(text), full_text=text
423 cursor_line=0, cursor_pos=len(text), full_text=text
418 )["IPCompleter.file_matcher"]["completions"]
424 )["IPCompleter.file_matcher"]["completions"]
419
425
420 with TemporaryWorkingDirectory():
426 with TemporaryWorkingDirectory():
421 name = "foo'bar"
427 name = "foo'bar"
422 open(name, "w", encoding="utf-8").close()
428 open(name, "w", encoding="utf-8").close()
423
429
424 # Don't escape Windows
430 # Don't escape Windows
425 escaped = name if sys.platform == "win32" else "foo\\'bar"
431 escaped = name if sys.platform == "win32" else "foo\\'bar"
426
432
427 # Single quote matches embedded single quote
433 # Single quote matches embedded single quote
428 c = _("open('foo")[0]
434 c = _("open('foo")[0]
429 self.assertEqual(c.text, escaped)
435 self.assertEqual(c.text, escaped)
430
436
431 # Double quote requires no escape
437 # Double quote requires no escape
432 c = _('open("foo')[0]
438 c = _('open("foo')[0]
433 self.assertEqual(c.text, name)
439 self.assertEqual(c.text, name)
434
440
435 # No quote requires an escape
441 # No quote requires an escape
436 c = _("%ls foo")[0]
442 c = _("%ls foo")[0]
437 self.assertEqual(c.text, escaped)
443 self.assertEqual(c.text, escaped)
438
444
439 @pytest.mark.xfail(
445 @pytest.mark.xfail(
440 sys.version_info.releaselevel in ("alpha",),
446 sys.version_info.releaselevel in ("alpha",),
441 reason="Parso does not yet parse 3.13",
447 reason="Parso does not yet parse 3.13",
442 )
448 )
443 def test_all_completions_dups(self):
449 def test_all_completions_dups(self):
444 """
450 """
445 Make sure the output of `IPCompleter.all_completions` does not have
451 Make sure the output of `IPCompleter.all_completions` does not have
446 duplicated prefixes.
452 duplicated prefixes.
447 """
453 """
448 ip = get_ipython()
454 ip = get_ipython()
449 c = ip.Completer
455 c = ip.Completer
450 ip.ex("class TestClass():\n\ta=1\n\ta1=2")
456 ip.ex("class TestClass():\n\ta=1\n\ta1=2")
451 for jedi_status in [True, False]:
457 for jedi_status in [True, False]:
452 with provisionalcompleter():
458 with provisionalcompleter():
453 ip.Completer.use_jedi = jedi_status
459 ip.Completer.use_jedi = jedi_status
454 matches = c.all_completions("TestCl")
460 matches = c.all_completions("TestCl")
455 assert matches == ["TestClass"], (jedi_status, matches)
461 assert matches == ["TestClass"], (jedi_status, matches)
456 matches = c.all_completions("TestClass.")
462 matches = c.all_completions("TestClass.")
457 assert len(matches) > 2, (jedi_status, matches)
463 assert len(matches) > 2, (jedi_status, matches)
458 matches = c.all_completions("TestClass.a")
464 matches = c.all_completions("TestClass.a")
459 assert matches == ['TestClass.a', 'TestClass.a1'], jedi_status
465 assert matches == ['TestClass.a', 'TestClass.a1'], jedi_status
460
466
461 @pytest.mark.xfail(
467 @pytest.mark.xfail(
462 sys.version_info.releaselevel in ("alpha",),
468 sys.version_info.releaselevel in ("alpha",),
463 reason="Parso does not yet parse 3.13",
469 reason="Parso does not yet parse 3.13",
464 )
470 )
465 def test_jedi(self):
471 def test_jedi(self):
466 """
472 """
467 A couple of issue we had with Jedi
473 A couple of issue we had with Jedi
468 """
474 """
469 ip = get_ipython()
475 ip = get_ipython()
470
476
471 def _test_complete(reason, s, comp, start=None, end=None):
477 def _test_complete(reason, s, comp, start=None, end=None):
472 l = len(s)
478 l = len(s)
473 start = start if start is not None else l
479 start = start if start is not None else l
474 end = end if end is not None else l
480 end = end if end is not None else l
475 with provisionalcompleter():
481 with provisionalcompleter():
476 ip.Completer.use_jedi = True
482 ip.Completer.use_jedi = True
477 completions = set(ip.Completer.completions(s, l))
483 completions = set(ip.Completer.completions(s, l))
478 ip.Completer.use_jedi = False
484 ip.Completer.use_jedi = False
479 assert Completion(start, end, comp) in completions, reason
485 assert Completion(start, end, comp) in completions, reason
480
486
481 def _test_not_complete(reason, s, comp):
487 def _test_not_complete(reason, s, comp):
482 l = len(s)
488 l = len(s)
483 with provisionalcompleter():
489 with provisionalcompleter():
484 ip.Completer.use_jedi = True
490 ip.Completer.use_jedi = True
485 completions = set(ip.Completer.completions(s, l))
491 completions = set(ip.Completer.completions(s, l))
486 ip.Completer.use_jedi = False
492 ip.Completer.use_jedi = False
487 assert Completion(l, l, comp) not in completions, reason
493 assert Completion(l, l, comp) not in completions, reason
488
494
489 import jedi
495 import jedi
490
496
491 jedi_version = tuple(int(i) for i in jedi.__version__.split(".")[:3])
497 jedi_version = tuple(int(i) for i in jedi.__version__.split(".")[:3])
492 if jedi_version > (0, 10):
498 if jedi_version > (0, 10):
493 _test_complete("jedi >0.9 should complete and not crash", "a=1;a.", "real")
499 _test_complete("jedi >0.9 should complete and not crash", "a=1;a.", "real")
494 _test_complete("can infer first argument", 'a=(1,"foo");a[0].', "real")
500 _test_complete("can infer first argument", 'a=(1,"foo");a[0].', "real")
495 _test_complete("can infer second argument", 'a=(1,"foo");a[1].', "capitalize")
501 _test_complete("can infer second argument", 'a=(1,"foo");a[1].', "capitalize")
496 _test_complete("cover duplicate completions", "im", "import", 0, 2)
502 _test_complete("cover duplicate completions", "im", "import", 0, 2)
497
503
498 _test_not_complete("does not mix types", 'a=(1,"foo");a[0].', "capitalize")
504 _test_not_complete("does not mix types", 'a=(1,"foo");a[0].', "capitalize")
499
505
500 @pytest.mark.xfail(
506 @pytest.mark.xfail(
501 sys.version_info.releaselevel in ("alpha",),
507 sys.version_info.releaselevel in ("alpha",),
502 reason="Parso does not yet parse 3.13",
508 reason="Parso does not yet parse 3.13",
503 )
509 )
504 def test_completion_have_signature(self):
510 def test_completion_have_signature(self):
505 """
511 """
506 Lets make sure jedi is capable of pulling out the signature of the function we are completing.
512 Lets make sure jedi is capable of pulling out the signature of the function we are completing.
507 """
513 """
508 ip = get_ipython()
514 ip = get_ipython()
509 with provisionalcompleter():
515 with provisionalcompleter():
510 ip.Completer.use_jedi = True
516 ip.Completer.use_jedi = True
511 completions = ip.Completer.completions("ope", 3)
517 completions = ip.Completer.completions("ope", 3)
512 c = next(completions) # should be `open`
518 c = next(completions) # should be `open`
513 ip.Completer.use_jedi = False
519 ip.Completer.use_jedi = False
514 assert "file" in c.signature, "Signature of function was not found by completer"
520 assert "file" in c.signature, "Signature of function was not found by completer"
515 assert (
521 assert (
516 "encoding" in c.signature
522 "encoding" in c.signature
517 ), "Signature of function was not found by completer"
523 ), "Signature of function was not found by completer"
518
524
519 @pytest.mark.xfail(
525 @pytest.mark.xfail(
520 sys.version_info.releaselevel in ("alpha",),
526 sys.version_info.releaselevel in ("alpha",),
521 reason="Parso does not yet parse 3.13",
527 reason="Parso does not yet parse 3.13",
522 )
528 )
523 def test_completions_have_type(self):
529 def test_completions_have_type(self):
524 """
530 """
525 Lets make sure matchers provide completion type.
531 Lets make sure matchers provide completion type.
526 """
532 """
527 ip = get_ipython()
533 ip = get_ipython()
528 with provisionalcompleter():
534 with provisionalcompleter():
529 ip.Completer.use_jedi = False
535 ip.Completer.use_jedi = False
530 completions = ip.Completer.completions("%tim", 3)
536 completions = ip.Completer.completions("%tim", 3)
531 c = next(completions) # should be `%time` or similar
537 c = next(completions) # should be `%time` or similar
532 assert c.type == "magic", "Type of magic was not assigned by completer"
538 assert c.type == "magic", "Type of magic was not assigned by completer"
533
539
534 @pytest.mark.xfail(reason="Known failure on jedi<=0.18.0")
540 @pytest.mark.xfail(
541 parse(version("jedi")) <= parse("0.18.0"),
542 reason="Known failure on jedi<=0.18.0",
543 strict=True,
544 )
535 def test_deduplicate_completions(self):
545 def test_deduplicate_completions(self):
536 """
546 """
537 Test that completions are correctly deduplicated (even if ranges are not the same)
547 Test that completions are correctly deduplicated (even if ranges are not the same)
538 """
548 """
539 ip = get_ipython()
549 ip = get_ipython()
540 ip.ex(
550 ip.ex(
541 textwrap.dedent(
551 textwrap.dedent(
542 """
552 """
543 class Z:
553 class Z:
544 zoo = 1
554 zoo = 1
545 """
555 """
546 )
556 )
547 )
557 )
548 with provisionalcompleter():
558 with provisionalcompleter():
549 ip.Completer.use_jedi = True
559 ip.Completer.use_jedi = True
550 l = list(
560 l = list(
551 _deduplicate_completions("Z.z", ip.Completer.completions("Z.z", 3))
561 _deduplicate_completions("Z.z", ip.Completer.completions("Z.z", 3))
552 )
562 )
553 ip.Completer.use_jedi = False
563 ip.Completer.use_jedi = False
554
564
555 assert len(l) == 1, "Completions (Z.z<tab>) correctly deduplicate: %s " % l
565 assert len(l) == 1, "Completions (Z.z<tab>) correctly deduplicate: %s " % l
556 assert l[0].text == "zoo" # and not `it.accumulate`
566 assert l[0].text == "zoo" # and not `it.accumulate`
557
567
558 @pytest.mark.xfail(
568 @pytest.mark.xfail(
559 sys.version_info.releaselevel in ("alpha",),
569 sys.version_info.releaselevel in ("alpha",),
560 reason="Parso does not yet parse 3.13",
570 reason="Parso does not yet parse 3.13",
561 )
571 )
562 def test_greedy_completions(self):
572 def test_greedy_completions(self):
563 """
573 """
564 Test the capability of the Greedy completer.
574 Test the capability of the Greedy completer.
565
575
566 Most of the test here does not really show off the greedy completer, for proof
576 Most of the test here does not really show off the greedy completer, for proof
567 each of the text below now pass with Jedi. The greedy completer is capable of more.
577 each of the text below now pass with Jedi. The greedy completer is capable of more.
568
578
569 See the :any:`test_dict_key_completion_contexts`
579 See the :any:`test_dict_key_completion_contexts`
570
580
571 """
581 """
572 ip = get_ipython()
582 ip = get_ipython()
573 ip.ex("a=list(range(5))")
583 ip.ex("a=list(range(5))")
574 ip.ex("d = {'a b': str}")
584 ip.ex("d = {'a b': str}")
575 _, c = ip.complete(".", line="a[0].")
585 _, c = ip.complete(".", line="a[0].")
576 self.assertFalse(".real" in c, "Shouldn't have completed on a[0]: %s" % c)
586 self.assertFalse(".real" in c, "Shouldn't have completed on a[0]: %s" % c)
577
587
578 def _(line, cursor_pos, expect, message, completion):
588 def _(line, cursor_pos, expect, message, completion):
579 with greedy_completion(), provisionalcompleter():
589 with greedy_completion(), provisionalcompleter():
580 ip.Completer.use_jedi = False
590 ip.Completer.use_jedi = False
581 _, c = ip.complete(".", line=line, cursor_pos=cursor_pos)
591 _, c = ip.complete(".", line=line, cursor_pos=cursor_pos)
582 self.assertIn(expect, c, message % c)
592 self.assertIn(expect, c, message % c)
583
593
584 ip.Completer.use_jedi = True
594 ip.Completer.use_jedi = True
585 with provisionalcompleter():
595 with provisionalcompleter():
586 completions = ip.Completer.completions(line, cursor_pos)
596 completions = ip.Completer.completions(line, cursor_pos)
587 self.assertIn(completion, completions)
597 self.assertIn(completion, completions)
588
598
589 with provisionalcompleter():
599 with provisionalcompleter():
590 _(
600 _(
591 "a[0].",
601 "a[0].",
592 5,
602 5,
593 ".real",
603 ".real",
594 "Should have completed on a[0].: %s",
604 "Should have completed on a[0].: %s",
595 Completion(5, 5, "real"),
605 Completion(5, 5, "real"),
596 )
606 )
597 _(
607 _(
598 "a[0].r",
608 "a[0].r",
599 6,
609 6,
600 ".real",
610 ".real",
601 "Should have completed on a[0].r: %s",
611 "Should have completed on a[0].r: %s",
602 Completion(5, 6, "real"),
612 Completion(5, 6, "real"),
603 )
613 )
604
614
605 _(
615 _(
606 "a[0].from_",
616 "a[0].from_",
607 10,
617 10,
608 ".from_bytes",
618 ".from_bytes",
609 "Should have completed on a[0].from_: %s",
619 "Should have completed on a[0].from_: %s",
610 Completion(5, 10, "from_bytes"),
620 Completion(5, 10, "from_bytes"),
611 )
621 )
612 _(
622 _(
613 "assert str.star",
623 "assert str.star",
614 14,
624 14,
615 "str.startswith",
625 "str.startswith",
616 "Should have completed on `assert str.star`: %s",
626 "Should have completed on `assert str.star`: %s",
617 Completion(11, 14, "startswith"),
627 Completion(11, 14, "startswith"),
618 )
628 )
619 _(
629 _(
620 "d['a b'].str",
630 "d['a b'].str",
621 12,
631 12,
622 ".strip",
632 ".strip",
623 "Should have completed on `d['a b'].str`: %s",
633 "Should have completed on `d['a b'].str`: %s",
624 Completion(9, 12, "strip"),
634 Completion(9, 12, "strip"),
625 )
635 )
626
636
627 def test_omit__names(self):
637 def test_omit__names(self):
628 # also happens to test IPCompleter as a configurable
638 # also happens to test IPCompleter as a configurable
629 ip = get_ipython()
639 ip = get_ipython()
630 ip._hidden_attr = 1
640 ip._hidden_attr = 1
631 ip._x = {}
641 ip._x = {}
632 c = ip.Completer
642 c = ip.Completer
633 ip.ex("ip=get_ipython()")
643 ip.ex("ip=get_ipython()")
634 cfg = Config()
644 cfg = Config()
635 cfg.IPCompleter.omit__names = 0
645 cfg.IPCompleter.omit__names = 0
636 c.update_config(cfg)
646 c.update_config(cfg)
637 with provisionalcompleter():
647 with provisionalcompleter():
638 c.use_jedi = False
648 c.use_jedi = False
639 s, matches = c.complete("ip.")
649 s, matches = c.complete("ip.")
640 self.assertIn("ip.__str__", matches)
650 self.assertIn("ip.__str__", matches)
641 self.assertIn("ip._hidden_attr", matches)
651 self.assertIn("ip._hidden_attr", matches)
642
652
643 # c.use_jedi = True
653 # c.use_jedi = True
644 # completions = set(c.completions('ip.', 3))
654 # completions = set(c.completions('ip.', 3))
645 # self.assertIn(Completion(3, 3, '__str__'), completions)
655 # self.assertIn(Completion(3, 3, '__str__'), completions)
646 # self.assertIn(Completion(3,3, "_hidden_attr"), completions)
656 # self.assertIn(Completion(3,3, "_hidden_attr"), completions)
647
657
648 cfg = Config()
658 cfg = Config()
649 cfg.IPCompleter.omit__names = 1
659 cfg.IPCompleter.omit__names = 1
650 c.update_config(cfg)
660 c.update_config(cfg)
651 with provisionalcompleter():
661 with provisionalcompleter():
652 c.use_jedi = False
662 c.use_jedi = False
653 s, matches = c.complete("ip.")
663 s, matches = c.complete("ip.")
654 self.assertNotIn("ip.__str__", matches)
664 self.assertNotIn("ip.__str__", matches)
655 # self.assertIn('ip._hidden_attr', matches)
665 # self.assertIn('ip._hidden_attr', matches)
656
666
657 # c.use_jedi = True
667 # c.use_jedi = True
658 # completions = set(c.completions('ip.', 3))
668 # completions = set(c.completions('ip.', 3))
659 # self.assertNotIn(Completion(3,3,'__str__'), completions)
669 # self.assertNotIn(Completion(3,3,'__str__'), completions)
660 # self.assertIn(Completion(3,3, "_hidden_attr"), completions)
670 # self.assertIn(Completion(3,3, "_hidden_attr"), completions)
661
671
662 cfg = Config()
672 cfg = Config()
663 cfg.IPCompleter.omit__names = 2
673 cfg.IPCompleter.omit__names = 2
664 c.update_config(cfg)
674 c.update_config(cfg)
665 with provisionalcompleter():
675 with provisionalcompleter():
666 c.use_jedi = False
676 c.use_jedi = False
667 s, matches = c.complete("ip.")
677 s, matches = c.complete("ip.")
668 self.assertNotIn("ip.__str__", matches)
678 self.assertNotIn("ip.__str__", matches)
669 self.assertNotIn("ip._hidden_attr", matches)
679 self.assertNotIn("ip._hidden_attr", matches)
670
680
671 # c.use_jedi = True
681 # c.use_jedi = True
672 # completions = set(c.completions('ip.', 3))
682 # completions = set(c.completions('ip.', 3))
673 # self.assertNotIn(Completion(3,3,'__str__'), completions)
683 # self.assertNotIn(Completion(3,3,'__str__'), completions)
674 # self.assertNotIn(Completion(3,3, "_hidden_attr"), completions)
684 # self.assertNotIn(Completion(3,3, "_hidden_attr"), completions)
675
685
676 with provisionalcompleter():
686 with provisionalcompleter():
677 c.use_jedi = False
687 c.use_jedi = False
678 s, matches = c.complete("ip._x.")
688 s, matches = c.complete("ip._x.")
679 self.assertIn("ip._x.keys", matches)
689 self.assertIn("ip._x.keys", matches)
680
690
681 # c.use_jedi = True
691 # c.use_jedi = True
682 # completions = set(c.completions('ip._x.', 6))
692 # completions = set(c.completions('ip._x.', 6))
683 # self.assertIn(Completion(6,6, "keys"), completions)
693 # self.assertIn(Completion(6,6, "keys"), completions)
684
694
685 del ip._hidden_attr
695 del ip._hidden_attr
686 del ip._x
696 del ip._x
687
697
688 def test_limit_to__all__False_ok(self):
698 def test_limit_to__all__False_ok(self):
689 """
699 """
690 Limit to all is deprecated, once we remove it this test can go away.
700 Limit to all is deprecated, once we remove it this test can go away.
691 """
701 """
692 ip = get_ipython()
702 ip = get_ipython()
693 c = ip.Completer
703 c = ip.Completer
694 c.use_jedi = False
704 c.use_jedi = False
695 ip.ex("class D: x=24")
705 ip.ex("class D: x=24")
696 ip.ex("d=D()")
706 ip.ex("d=D()")
697 cfg = Config()
707 cfg = Config()
698 cfg.IPCompleter.limit_to__all__ = False
708 cfg.IPCompleter.limit_to__all__ = False
699 c.update_config(cfg)
709 c.update_config(cfg)
700 s, matches = c.complete("d.")
710 s, matches = c.complete("d.")
701 self.assertIn("d.x", matches)
711 self.assertIn("d.x", matches)
702
712
703 def test_get__all__entries_ok(self):
713 def test_get__all__entries_ok(self):
704 class A:
714 class A:
705 __all__ = ["x", 1]
715 __all__ = ["x", 1]
706
716
707 words = completer.get__all__entries(A())
717 words = completer.get__all__entries(A())
708 self.assertEqual(words, ["x"])
718 self.assertEqual(words, ["x"])
709
719
710 def test_get__all__entries_no__all__ok(self):
720 def test_get__all__entries_no__all__ok(self):
711 class A:
721 class A:
712 pass
722 pass
713
723
714 words = completer.get__all__entries(A())
724 words = completer.get__all__entries(A())
715 self.assertEqual(words, [])
725 self.assertEqual(words, [])
716
726
717 def test_func_kw_completions(self):
727 def test_func_kw_completions(self):
718 ip = get_ipython()
728 ip = get_ipython()
719 c = ip.Completer
729 c = ip.Completer
720 c.use_jedi = False
730 c.use_jedi = False
721 ip.ex("def myfunc(a=1,b=2): return a+b")
731 ip.ex("def myfunc(a=1,b=2): return a+b")
722 s, matches = c.complete(None, "myfunc(1,b")
732 s, matches = c.complete(None, "myfunc(1,b")
723 self.assertIn("b=", matches)
733 self.assertIn("b=", matches)
724 # Simulate completing with cursor right after b (pos==10):
734 # Simulate completing with cursor right after b (pos==10):
725 s, matches = c.complete(None, "myfunc(1,b)", 10)
735 s, matches = c.complete(None, "myfunc(1,b)", 10)
726 self.assertIn("b=", matches)
736 self.assertIn("b=", matches)
727 s, matches = c.complete(None, 'myfunc(a="escaped\\")string",b')
737 s, matches = c.complete(None, 'myfunc(a="escaped\\")string",b')
728 self.assertIn("b=", matches)
738 self.assertIn("b=", matches)
729 # builtin function
739 # builtin function
730 s, matches = c.complete(None, "min(k, k")
740 s, matches = c.complete(None, "min(k, k")
731 self.assertIn("key=", matches)
741 self.assertIn("key=", matches)
732
742
733 def test_default_arguments_from_docstring(self):
743 def test_default_arguments_from_docstring(self):
734 ip = get_ipython()
744 ip = get_ipython()
735 c = ip.Completer
745 c = ip.Completer
736 kwd = c._default_arguments_from_docstring("min(iterable[, key=func]) -> value")
746 kwd = c._default_arguments_from_docstring("min(iterable[, key=func]) -> value")
737 self.assertEqual(kwd, ["key"])
747 self.assertEqual(kwd, ["key"])
738 # with cython type etc
748 # with cython type etc
739 kwd = c._default_arguments_from_docstring(
749 kwd = c._default_arguments_from_docstring(
740 "Minuit.migrad(self, int ncall=10000, resume=True, int nsplit=1)\n"
750 "Minuit.migrad(self, int ncall=10000, resume=True, int nsplit=1)\n"
741 )
751 )
742 self.assertEqual(kwd, ["ncall", "resume", "nsplit"])
752 self.assertEqual(kwd, ["ncall", "resume", "nsplit"])
743 # white spaces
753 # white spaces
744 kwd = c._default_arguments_from_docstring(
754 kwd = c._default_arguments_from_docstring(
745 "\n Minuit.migrad(self, int ncall=10000, resume=True, int nsplit=1)\n"
755 "\n Minuit.migrad(self, int ncall=10000, resume=True, int nsplit=1)\n"
746 )
756 )
747 self.assertEqual(kwd, ["ncall", "resume", "nsplit"])
757 self.assertEqual(kwd, ["ncall", "resume", "nsplit"])
748
758
749 def test_line_magics(self):
759 def test_line_magics(self):
750 ip = get_ipython()
760 ip = get_ipython()
751 c = ip.Completer
761 c = ip.Completer
752 s, matches = c.complete(None, "lsmag")
762 s, matches = c.complete(None, "lsmag")
753 self.assertIn("%lsmagic", matches)
763 self.assertIn("%lsmagic", matches)
754 s, matches = c.complete(None, "%lsmag")
764 s, matches = c.complete(None, "%lsmag")
755 self.assertIn("%lsmagic", matches)
765 self.assertIn("%lsmagic", matches)
756
766
757 def test_cell_magics(self):
767 def test_cell_magics(self):
758 from IPython.core.magic import register_cell_magic
768 from IPython.core.magic import register_cell_magic
759
769
760 @register_cell_magic
770 @register_cell_magic
761 def _foo_cellm(line, cell):
771 def _foo_cellm(line, cell):
762 pass
772 pass
763
773
764 ip = get_ipython()
774 ip = get_ipython()
765 c = ip.Completer
775 c = ip.Completer
766
776
767 s, matches = c.complete(None, "_foo_ce")
777 s, matches = c.complete(None, "_foo_ce")
768 self.assertIn("%%_foo_cellm", matches)
778 self.assertIn("%%_foo_cellm", matches)
769 s, matches = c.complete(None, "%%_foo_ce")
779 s, matches = c.complete(None, "%%_foo_ce")
770 self.assertIn("%%_foo_cellm", matches)
780 self.assertIn("%%_foo_cellm", matches)
771
781
772 def test_line_cell_magics(self):
782 def test_line_cell_magics(self):
773 from IPython.core.magic import register_line_cell_magic
783 from IPython.core.magic import register_line_cell_magic
774
784
775 @register_line_cell_magic
785 @register_line_cell_magic
776 def _bar_cellm(line, cell):
786 def _bar_cellm(line, cell):
777 pass
787 pass
778
788
779 ip = get_ipython()
789 ip = get_ipython()
780 c = ip.Completer
790 c = ip.Completer
781
791
782 # The policy here is trickier, see comments in completion code. The
792 # The policy here is trickier, see comments in completion code. The
783 # returned values depend on whether the user passes %% or not explicitly,
793 # returned values depend on whether the user passes %% or not explicitly,
784 # and this will show a difference if the same name is both a line and cell
794 # and this will show a difference if the same name is both a line and cell
785 # magic.
795 # magic.
786 s, matches = c.complete(None, "_bar_ce")
796 s, matches = c.complete(None, "_bar_ce")
787 self.assertIn("%_bar_cellm", matches)
797 self.assertIn("%_bar_cellm", matches)
788 self.assertIn("%%_bar_cellm", matches)
798 self.assertIn("%%_bar_cellm", matches)
789 s, matches = c.complete(None, "%_bar_ce")
799 s, matches = c.complete(None, "%_bar_ce")
790 self.assertIn("%_bar_cellm", matches)
800 self.assertIn("%_bar_cellm", matches)
791 self.assertIn("%%_bar_cellm", matches)
801 self.assertIn("%%_bar_cellm", matches)
792 s, matches = c.complete(None, "%%_bar_ce")
802 s, matches = c.complete(None, "%%_bar_ce")
793 self.assertNotIn("%_bar_cellm", matches)
803 self.assertNotIn("%_bar_cellm", matches)
794 self.assertIn("%%_bar_cellm", matches)
804 self.assertIn("%%_bar_cellm", matches)
795
805
796 def test_magic_completion_order(self):
806 def test_magic_completion_order(self):
797 ip = get_ipython()
807 ip = get_ipython()
798 c = ip.Completer
808 c = ip.Completer
799
809
800 # Test ordering of line and cell magics.
810 # Test ordering of line and cell magics.
801 text, matches = c.complete("timeit")
811 text, matches = c.complete("timeit")
802 self.assertEqual(matches, ["%timeit", "%%timeit"])
812 self.assertEqual(matches, ["%timeit", "%%timeit"])
803
813
804 def test_magic_completion_shadowing(self):
814 def test_magic_completion_shadowing(self):
805 ip = get_ipython()
815 ip = get_ipython()
806 c = ip.Completer
816 c = ip.Completer
807 c.use_jedi = False
817 c.use_jedi = False
808
818
809 # Before importing matplotlib, %matplotlib magic should be the only option.
819 # Before importing matplotlib, %matplotlib magic should be the only option.
810 text, matches = c.complete("mat")
820 text, matches = c.complete("mat")
811 self.assertEqual(matches, ["%matplotlib"])
821 self.assertEqual(matches, ["%matplotlib"])
812
822
813 # The newly introduced name should shadow the magic.
823 # The newly introduced name should shadow the magic.
814 ip.run_cell("matplotlib = 1")
824 ip.run_cell("matplotlib = 1")
815 text, matches = c.complete("mat")
825 text, matches = c.complete("mat")
816 self.assertEqual(matches, ["matplotlib"])
826 self.assertEqual(matches, ["matplotlib"])
817
827
818 # After removing matplotlib from namespace, the magic should again be
828 # After removing matplotlib from namespace, the magic should again be
819 # the only option.
829 # the only option.
820 del ip.user_ns["matplotlib"]
830 del ip.user_ns["matplotlib"]
821 text, matches = c.complete("mat")
831 text, matches = c.complete("mat")
822 self.assertEqual(matches, ["%matplotlib"])
832 self.assertEqual(matches, ["%matplotlib"])
823
833
824 def test_magic_completion_shadowing_explicit(self):
834 def test_magic_completion_shadowing_explicit(self):
825 """
835 """
826 If the user try to complete a shadowed magic, and explicit % start should
836 If the user try to complete a shadowed magic, and explicit % start should
827 still return the completions.
837 still return the completions.
828 """
838 """
829 ip = get_ipython()
839 ip = get_ipython()
830 c = ip.Completer
840 c = ip.Completer
831
841
832 # Before importing matplotlib, %matplotlib magic should be the only option.
842 # Before importing matplotlib, %matplotlib magic should be the only option.
833 text, matches = c.complete("%mat")
843 text, matches = c.complete("%mat")
834 self.assertEqual(matches, ["%matplotlib"])
844 self.assertEqual(matches, ["%matplotlib"])
835
845
836 ip.run_cell("matplotlib = 1")
846 ip.run_cell("matplotlib = 1")
837
847
838 # After removing matplotlib from namespace, the magic should still be
848 # After removing matplotlib from namespace, the magic should still be
839 # the only option.
849 # the only option.
840 text, matches = c.complete("%mat")
850 text, matches = c.complete("%mat")
841 self.assertEqual(matches, ["%matplotlib"])
851 self.assertEqual(matches, ["%matplotlib"])
842
852
843 def test_magic_config(self):
853 def test_magic_config(self):
844 ip = get_ipython()
854 ip = get_ipython()
845 c = ip.Completer
855 c = ip.Completer
846
856
847 s, matches = c.complete(None, "conf")
857 s, matches = c.complete(None, "conf")
848 self.assertIn("%config", matches)
858 self.assertIn("%config", matches)
849 s, matches = c.complete(None, "conf")
859 s, matches = c.complete(None, "conf")
850 self.assertNotIn("AliasManager", matches)
860 self.assertNotIn("AliasManager", matches)
851 s, matches = c.complete(None, "config ")
861 s, matches = c.complete(None, "config ")
852 self.assertIn("AliasManager", matches)
862 self.assertIn("AliasManager", matches)
853 s, matches = c.complete(None, "%config ")
863 s, matches = c.complete(None, "%config ")
854 self.assertIn("AliasManager", matches)
864 self.assertIn("AliasManager", matches)
855 s, matches = c.complete(None, "config Ali")
865 s, matches = c.complete(None, "config Ali")
856 self.assertListEqual(["AliasManager"], matches)
866 self.assertListEqual(["AliasManager"], matches)
857 s, matches = c.complete(None, "%config Ali")
867 s, matches = c.complete(None, "%config Ali")
858 self.assertListEqual(["AliasManager"], matches)
868 self.assertListEqual(["AliasManager"], matches)
859 s, matches = c.complete(None, "config AliasManager")
869 s, matches = c.complete(None, "config AliasManager")
860 self.assertListEqual(["AliasManager"], matches)
870 self.assertListEqual(["AliasManager"], matches)
861 s, matches = c.complete(None, "%config AliasManager")
871 s, matches = c.complete(None, "%config AliasManager")
862 self.assertListEqual(["AliasManager"], matches)
872 self.assertListEqual(["AliasManager"], matches)
863 s, matches = c.complete(None, "config AliasManager.")
873 s, matches = c.complete(None, "config AliasManager.")
864 self.assertIn("AliasManager.default_aliases", matches)
874 self.assertIn("AliasManager.default_aliases", matches)
865 s, matches = c.complete(None, "%config AliasManager.")
875 s, matches = c.complete(None, "%config AliasManager.")
866 self.assertIn("AliasManager.default_aliases", matches)
876 self.assertIn("AliasManager.default_aliases", matches)
867 s, matches = c.complete(None, "config AliasManager.de")
877 s, matches = c.complete(None, "config AliasManager.de")
868 self.assertListEqual(["AliasManager.default_aliases"], matches)
878 self.assertListEqual(["AliasManager.default_aliases"], matches)
869 s, matches = c.complete(None, "config AliasManager.de")
879 s, matches = c.complete(None, "config AliasManager.de")
870 self.assertListEqual(["AliasManager.default_aliases"], matches)
880 self.assertListEqual(["AliasManager.default_aliases"], matches)
871
881
872 def test_magic_color(self):
882 def test_magic_color(self):
873 ip = get_ipython()
883 ip = get_ipython()
874 c = ip.Completer
884 c = ip.Completer
875
885
876 s, matches = c.complete(None, "colo")
886 s, matches = c.complete(None, "colo")
877 self.assertIn("%colors", matches)
887 self.assertIn("%colors", matches)
878 s, matches = c.complete(None, "colo")
888 s, matches = c.complete(None, "colo")
879 self.assertNotIn("NoColor", matches)
889 self.assertNotIn("NoColor", matches)
880 s, matches = c.complete(None, "%colors") # No trailing space
890 s, matches = c.complete(None, "%colors") # No trailing space
881 self.assertNotIn("NoColor", matches)
891 self.assertNotIn("NoColor", matches)
882 s, matches = c.complete(None, "colors ")
892 s, matches = c.complete(None, "colors ")
883 self.assertIn("NoColor", matches)
893 self.assertIn("NoColor", matches)
884 s, matches = c.complete(None, "%colors ")
894 s, matches = c.complete(None, "%colors ")
885 self.assertIn("NoColor", matches)
895 self.assertIn("NoColor", matches)
886 s, matches = c.complete(None, "colors NoCo")
896 s, matches = c.complete(None, "colors NoCo")
887 self.assertListEqual(["NoColor"], matches)
897 self.assertListEqual(["NoColor"], matches)
888 s, matches = c.complete(None, "%colors NoCo")
898 s, matches = c.complete(None, "%colors NoCo")
889 self.assertListEqual(["NoColor"], matches)
899 self.assertListEqual(["NoColor"], matches)
890
900
891 def test_match_dict_keys(self):
901 def test_match_dict_keys(self):
892 """
902 """
893 Test that match_dict_keys works on a couple of use case does return what
903 Test that match_dict_keys works on a couple of use case does return what
894 expected, and does not crash
904 expected, and does not crash
895 """
905 """
896 delims = " \t\n`!@#$^&*()=+[{]}\\|;:'\",<>?"
906 delims = " \t\n`!@#$^&*()=+[{]}\\|;:'\",<>?"
897
907
898 def match(*args, **kwargs):
908 def match(*args, **kwargs):
899 quote, offset, matches = match_dict_keys(*args, delims=delims, **kwargs)
909 quote, offset, matches = match_dict_keys(*args, delims=delims, **kwargs)
900 return quote, offset, list(matches)
910 return quote, offset, list(matches)
901
911
902 keys = ["foo", b"far"]
912 keys = ["foo", b"far"]
903 assert match(keys, "b'") == ("'", 2, ["far"])
913 assert match(keys, "b'") == ("'", 2, ["far"])
904 assert match(keys, "b'f") == ("'", 2, ["far"])
914 assert match(keys, "b'f") == ("'", 2, ["far"])
905 assert match(keys, 'b"') == ('"', 2, ["far"])
915 assert match(keys, 'b"') == ('"', 2, ["far"])
906 assert match(keys, 'b"f') == ('"', 2, ["far"])
916 assert match(keys, 'b"f') == ('"', 2, ["far"])
907
917
908 assert match(keys, "'") == ("'", 1, ["foo"])
918 assert match(keys, "'") == ("'", 1, ["foo"])
909 assert match(keys, "'f") == ("'", 1, ["foo"])
919 assert match(keys, "'f") == ("'", 1, ["foo"])
910 assert match(keys, '"') == ('"', 1, ["foo"])
920 assert match(keys, '"') == ('"', 1, ["foo"])
911 assert match(keys, '"f') == ('"', 1, ["foo"])
921 assert match(keys, '"f') == ('"', 1, ["foo"])
912
922
913 # Completion on first item of tuple
923 # Completion on first item of tuple
914 keys = [("foo", 1111), ("foo", 2222), (3333, "bar"), (3333, "test")]
924 keys = [("foo", 1111), ("foo", 2222), (3333, "bar"), (3333, "test")]
915 assert match(keys, "'f") == ("'", 1, ["foo"])
925 assert match(keys, "'f") == ("'", 1, ["foo"])
916 assert match(keys, "33") == ("", 0, ["3333"])
926 assert match(keys, "33") == ("", 0, ["3333"])
917
927
918 # Completion on numbers
928 # Completion on numbers
919 keys = [
929 keys = [
920 0xDEADBEEF,
930 0xDEADBEEF,
921 1111,
931 1111,
922 1234,
932 1234,
923 "1999",
933 "1999",
924 0b10101,
934 0b10101,
925 22,
935 22,
926 ] # 0xDEADBEEF = 3735928559; 0b10101 = 21
936 ] # 0xDEADBEEF = 3735928559; 0b10101 = 21
927 assert match(keys, "0xdead") == ("", 0, ["0xdeadbeef"])
937 assert match(keys, "0xdead") == ("", 0, ["0xdeadbeef"])
928 assert match(keys, "1") == ("", 0, ["1111", "1234"])
938 assert match(keys, "1") == ("", 0, ["1111", "1234"])
929 assert match(keys, "2") == ("", 0, ["21", "22"])
939 assert match(keys, "2") == ("", 0, ["21", "22"])
930 assert match(keys, "0b101") == ("", 0, ["0b10101", "0b10110"])
940 assert match(keys, "0b101") == ("", 0, ["0b10101", "0b10110"])
931
941
932 # Should yield on variables
942 # Should yield on variables
933 assert match(keys, "a_variable") == ("", 0, [])
943 assert match(keys, "a_variable") == ("", 0, [])
934
944
935 # Should pass over invalid literals
945 # Should pass over invalid literals
936 assert match(keys, "'' ''") == ("", 0, [])
946 assert match(keys, "'' ''") == ("", 0, [])
937
947
938 def test_match_dict_keys_tuple(self):
948 def test_match_dict_keys_tuple(self):
939 """
949 """
940 Test that match_dict_keys called with extra prefix works on a couple of use case,
950 Test that match_dict_keys called with extra prefix works on a couple of use case,
941 does return what expected, and does not crash.
951 does return what expected, and does not crash.
942 """
952 """
943 delims = " \t\n`!@#$^&*()=+[{]}\\|;:'\",<>?"
953 delims = " \t\n`!@#$^&*()=+[{]}\\|;:'\",<>?"
944
954
945 keys = [("foo", "bar"), ("foo", "oof"), ("foo", b"bar"), ('other', 'test')]
955 keys = [("foo", "bar"), ("foo", "oof"), ("foo", b"bar"), ('other', 'test')]
946
956
947 def match(*args, extra=None, **kwargs):
957 def match(*args, extra=None, **kwargs):
948 quote, offset, matches = match_dict_keys(
958 quote, offset, matches = match_dict_keys(
949 *args, delims=delims, extra_prefix=extra, **kwargs
959 *args, delims=delims, extra_prefix=extra, **kwargs
950 )
960 )
951 return quote, offset, list(matches)
961 return quote, offset, list(matches)
952
962
953 # Completion on first key == "foo"
963 # Completion on first key == "foo"
954 assert match(keys, "'", extra=("foo",)) == ("'", 1, ["bar", "oof"])
964 assert match(keys, "'", extra=("foo",)) == ("'", 1, ["bar", "oof"])
955 assert match(keys, '"', extra=("foo",)) == ('"', 1, ["bar", "oof"])
965 assert match(keys, '"', extra=("foo",)) == ('"', 1, ["bar", "oof"])
956 assert match(keys, "'o", extra=("foo",)) == ("'", 1, ["oof"])
966 assert match(keys, "'o", extra=("foo",)) == ("'", 1, ["oof"])
957 assert match(keys, '"o', extra=("foo",)) == ('"', 1, ["oof"])
967 assert match(keys, '"o', extra=("foo",)) == ('"', 1, ["oof"])
958 assert match(keys, "b'", extra=("foo",)) == ("'", 2, ["bar"])
968 assert match(keys, "b'", extra=("foo",)) == ("'", 2, ["bar"])
959 assert match(keys, 'b"', extra=("foo",)) == ('"', 2, ["bar"])
969 assert match(keys, 'b"', extra=("foo",)) == ('"', 2, ["bar"])
960 assert match(keys, "b'b", extra=("foo",)) == ("'", 2, ["bar"])
970 assert match(keys, "b'b", extra=("foo",)) == ("'", 2, ["bar"])
961 assert match(keys, 'b"b', extra=("foo",)) == ('"', 2, ["bar"])
971 assert match(keys, 'b"b', extra=("foo",)) == ('"', 2, ["bar"])
962
972
963 # No Completion
973 # No Completion
964 assert match(keys, "'", extra=("no_foo",)) == ("'", 1, [])
974 assert match(keys, "'", extra=("no_foo",)) == ("'", 1, [])
965 assert match(keys, "'", extra=("fo",)) == ("'", 1, [])
975 assert match(keys, "'", extra=("fo",)) == ("'", 1, [])
966
976
967 keys = [("foo1", "foo2", "foo3", "foo4"), ("foo1", "foo2", "bar", "foo4")]
977 keys = [("foo1", "foo2", "foo3", "foo4"), ("foo1", "foo2", "bar", "foo4")]
968 assert match(keys, "'foo", extra=("foo1",)) == ("'", 1, ["foo2"])
978 assert match(keys, "'foo", extra=("foo1",)) == ("'", 1, ["foo2"])
969 assert match(keys, "'foo", extra=("foo1", "foo2")) == ("'", 1, ["foo3"])
979 assert match(keys, "'foo", extra=("foo1", "foo2")) == ("'", 1, ["foo3"])
970 assert match(keys, "'foo", extra=("foo1", "foo2", "foo3")) == ("'", 1, ["foo4"])
980 assert match(keys, "'foo", extra=("foo1", "foo2", "foo3")) == ("'", 1, ["foo4"])
971 assert match(keys, "'foo", extra=("foo1", "foo2", "foo3", "foo4")) == (
981 assert match(keys, "'foo", extra=("foo1", "foo2", "foo3", "foo4")) == (
972 "'",
982 "'",
973 1,
983 1,
974 [],
984 [],
975 )
985 )
976
986
977 keys = [("foo", 1111), ("foo", "2222"), (3333, "bar"), (3333, 4444)]
987 keys = [("foo", 1111), ("foo", "2222"), (3333, "bar"), (3333, 4444)]
978 assert match(keys, "'", extra=("foo",)) == ("'", 1, ["2222"])
988 assert match(keys, "'", extra=("foo",)) == ("'", 1, ["2222"])
979 assert match(keys, "", extra=("foo",)) == ("", 0, ["1111", "'2222'"])
989 assert match(keys, "", extra=("foo",)) == ("", 0, ["1111", "'2222'"])
980 assert match(keys, "'", extra=(3333,)) == ("'", 1, ["bar"])
990 assert match(keys, "'", extra=(3333,)) == ("'", 1, ["bar"])
981 assert match(keys, "", extra=(3333,)) == ("", 0, ["'bar'", "4444"])
991 assert match(keys, "", extra=(3333,)) == ("", 0, ["'bar'", "4444"])
982 assert match(keys, "'", extra=("3333",)) == ("'", 1, [])
992 assert match(keys, "'", extra=("3333",)) == ("'", 1, [])
983 assert match(keys, "33") == ("", 0, ["3333"])
993 assert match(keys, "33") == ("", 0, ["3333"])
984
994
985 def test_dict_key_completion_closures(self):
995 def test_dict_key_completion_closures(self):
986 ip = get_ipython()
996 ip = get_ipython()
987 complete = ip.Completer.complete
997 complete = ip.Completer.complete
988 ip.Completer.auto_close_dict_keys = True
998 ip.Completer.auto_close_dict_keys = True
989
999
990 ip.user_ns["d"] = {
1000 ip.user_ns["d"] = {
991 # tuple only
1001 # tuple only
992 ("aa", 11): None,
1002 ("aa", 11): None,
993 # tuple and non-tuple
1003 # tuple and non-tuple
994 ("bb", 22): None,
1004 ("bb", 22): None,
995 "bb": None,
1005 "bb": None,
996 # non-tuple only
1006 # non-tuple only
997 "cc": None,
1007 "cc": None,
998 # numeric tuple only
1008 # numeric tuple only
999 (77, "x"): None,
1009 (77, "x"): None,
1000 # numeric tuple and non-tuple
1010 # numeric tuple and non-tuple
1001 (88, "y"): None,
1011 (88, "y"): None,
1002 88: None,
1012 88: None,
1003 # numeric non-tuple only
1013 # numeric non-tuple only
1004 99: None,
1014 99: None,
1005 }
1015 }
1006
1016
1007 _, matches = complete(line_buffer="d[")
1017 _, matches = complete(line_buffer="d[")
1008 # should append `, ` if matches a tuple only
1018 # should append `, ` if matches a tuple only
1009 self.assertIn("'aa', ", matches)
1019 self.assertIn("'aa', ", matches)
1010 # should not append anything if matches a tuple and an item
1020 # should not append anything if matches a tuple and an item
1011 self.assertIn("'bb'", matches)
1021 self.assertIn("'bb'", matches)
1012 # should append `]` if matches and item only
1022 # should append `]` if matches and item only
1013 self.assertIn("'cc']", matches)
1023 self.assertIn("'cc']", matches)
1014
1024
1015 # should append `, ` if matches a tuple only
1025 # should append `, ` if matches a tuple only
1016 self.assertIn("77, ", matches)
1026 self.assertIn("77, ", matches)
1017 # should not append anything if matches a tuple and an item
1027 # should not append anything if matches a tuple and an item
1018 self.assertIn("88", matches)
1028 self.assertIn("88", matches)
1019 # should append `]` if matches and item only
1029 # should append `]` if matches and item only
1020 self.assertIn("99]", matches)
1030 self.assertIn("99]", matches)
1021
1031
1022 _, matches = complete(line_buffer="d['aa', ")
1032 _, matches = complete(line_buffer="d['aa', ")
1023 # should restrict matches to those matching tuple prefix
1033 # should restrict matches to those matching tuple prefix
1024 self.assertIn("11]", matches)
1034 self.assertIn("11]", matches)
1025 self.assertNotIn("'bb'", matches)
1035 self.assertNotIn("'bb'", matches)
1026 self.assertNotIn("'bb', ", matches)
1036 self.assertNotIn("'bb', ", matches)
1027 self.assertNotIn("'bb']", matches)
1037 self.assertNotIn("'bb']", matches)
1028 self.assertNotIn("'cc'", matches)
1038 self.assertNotIn("'cc'", matches)
1029 self.assertNotIn("'cc', ", matches)
1039 self.assertNotIn("'cc', ", matches)
1030 self.assertNotIn("'cc']", matches)
1040 self.assertNotIn("'cc']", matches)
1031 ip.Completer.auto_close_dict_keys = False
1041 ip.Completer.auto_close_dict_keys = False
1032
1042
1033 def test_dict_key_completion_string(self):
1043 def test_dict_key_completion_string(self):
1034 """Test dictionary key completion for string keys"""
1044 """Test dictionary key completion for string keys"""
1035 ip = get_ipython()
1045 ip = get_ipython()
1036 complete = ip.Completer.complete
1046 complete = ip.Completer.complete
1037
1047
1038 ip.user_ns["d"] = {"abc": None}
1048 ip.user_ns["d"] = {"abc": None}
1039
1049
1040 # check completion at different stages
1050 # check completion at different stages
1041 _, matches = complete(line_buffer="d[")
1051 _, matches = complete(line_buffer="d[")
1042 self.assertIn("'abc'", matches)
1052 self.assertIn("'abc'", matches)
1043 self.assertNotIn("'abc']", matches)
1053 self.assertNotIn("'abc']", matches)
1044
1054
1045 _, matches = complete(line_buffer="d['")
1055 _, matches = complete(line_buffer="d['")
1046 self.assertIn("abc", matches)
1056 self.assertIn("abc", matches)
1047 self.assertNotIn("abc']", matches)
1057 self.assertNotIn("abc']", matches)
1048
1058
1049 _, matches = complete(line_buffer="d['a")
1059 _, matches = complete(line_buffer="d['a")
1050 self.assertIn("abc", matches)
1060 self.assertIn("abc", matches)
1051 self.assertNotIn("abc']", matches)
1061 self.assertNotIn("abc']", matches)
1052
1062
1053 # check use of different quoting
1063 # check use of different quoting
1054 _, matches = complete(line_buffer='d["')
1064 _, matches = complete(line_buffer='d["')
1055 self.assertIn("abc", matches)
1065 self.assertIn("abc", matches)
1056 self.assertNotIn('abc"]', matches)
1066 self.assertNotIn('abc"]', matches)
1057
1067
1058 _, matches = complete(line_buffer='d["a')
1068 _, matches = complete(line_buffer='d["a')
1059 self.assertIn("abc", matches)
1069 self.assertIn("abc", matches)
1060 self.assertNotIn('abc"]', matches)
1070 self.assertNotIn('abc"]', matches)
1061
1071
1062 # check sensitivity to following context
1072 # check sensitivity to following context
1063 _, matches = complete(line_buffer="d[]", cursor_pos=2)
1073 _, matches = complete(line_buffer="d[]", cursor_pos=2)
1064 self.assertIn("'abc'", matches)
1074 self.assertIn("'abc'", matches)
1065
1075
1066 _, matches = complete(line_buffer="d['']", cursor_pos=3)
1076 _, matches = complete(line_buffer="d['']", cursor_pos=3)
1067 self.assertIn("abc", matches)
1077 self.assertIn("abc", matches)
1068 self.assertNotIn("abc'", matches)
1078 self.assertNotIn("abc'", matches)
1069 self.assertNotIn("abc']", matches)
1079 self.assertNotIn("abc']", matches)
1070
1080
1071 # check multiple solutions are correctly returned and that noise is not
1081 # check multiple solutions are correctly returned and that noise is not
1072 ip.user_ns["d"] = {
1082 ip.user_ns["d"] = {
1073 "abc": None,
1083 "abc": None,
1074 "abd": None,
1084 "abd": None,
1075 "bad": None,
1085 "bad": None,
1076 object(): None,
1086 object(): None,
1077 5: None,
1087 5: None,
1078 ("abe", None): None,
1088 ("abe", None): None,
1079 (None, "abf"): None
1089 (None, "abf"): None
1080 }
1090 }
1081
1091
1082 _, matches = complete(line_buffer="d['a")
1092 _, matches = complete(line_buffer="d['a")
1083 self.assertIn("abc", matches)
1093 self.assertIn("abc", matches)
1084 self.assertIn("abd", matches)
1094 self.assertIn("abd", matches)
1085 self.assertNotIn("bad", matches)
1095 self.assertNotIn("bad", matches)
1086 self.assertNotIn("abe", matches)
1096 self.assertNotIn("abe", matches)
1087 self.assertNotIn("abf", matches)
1097 self.assertNotIn("abf", matches)
1088 assert not any(m.endswith(("]", '"', "'")) for m in matches), matches
1098 assert not any(m.endswith(("]", '"', "'")) for m in matches), matches
1089
1099
1090 # check escaping and whitespace
1100 # check escaping and whitespace
1091 ip.user_ns["d"] = {"a\nb": None, "a'b": None, 'a"b': None, "a word": None}
1101 ip.user_ns["d"] = {"a\nb": None, "a'b": None, 'a"b': None, "a word": None}
1092 _, matches = complete(line_buffer="d['a")
1102 _, matches = complete(line_buffer="d['a")
1093 self.assertIn("a\\nb", matches)
1103 self.assertIn("a\\nb", matches)
1094 self.assertIn("a\\'b", matches)
1104 self.assertIn("a\\'b", matches)
1095 self.assertIn('a"b', matches)
1105 self.assertIn('a"b', matches)
1096 self.assertIn("a word", matches)
1106 self.assertIn("a word", matches)
1097 assert not any(m.endswith(("]", '"', "'")) for m in matches), matches
1107 assert not any(m.endswith(("]", '"', "'")) for m in matches), matches
1098
1108
1099 # - can complete on non-initial word of the string
1109 # - can complete on non-initial word of the string
1100 _, matches = complete(line_buffer="d['a w")
1110 _, matches = complete(line_buffer="d['a w")
1101 self.assertIn("word", matches)
1111 self.assertIn("word", matches)
1102
1112
1103 # - understands quote escaping
1113 # - understands quote escaping
1104 _, matches = complete(line_buffer="d['a\\'")
1114 _, matches = complete(line_buffer="d['a\\'")
1105 self.assertIn("b", matches)
1115 self.assertIn("b", matches)
1106
1116
1107 # - default quoting should work like repr
1117 # - default quoting should work like repr
1108 _, matches = complete(line_buffer="d[")
1118 _, matches = complete(line_buffer="d[")
1109 self.assertIn('"a\'b"', matches)
1119 self.assertIn('"a\'b"', matches)
1110
1120
1111 # - when opening quote with ", possible to match with unescaped apostrophe
1121 # - when opening quote with ", possible to match with unescaped apostrophe
1112 _, matches = complete(line_buffer="d[\"a'")
1122 _, matches = complete(line_buffer="d[\"a'")
1113 self.assertIn("b", matches)
1123 self.assertIn("b", matches)
1114
1124
1115 # need to not split at delims that readline won't split at
1125 # need to not split at delims that readline won't split at
1116 if "-" not in ip.Completer.splitter.delims:
1126 if "-" not in ip.Completer.splitter.delims:
1117 ip.user_ns["d"] = {"before-after": None}
1127 ip.user_ns["d"] = {"before-after": None}
1118 _, matches = complete(line_buffer="d['before-af")
1128 _, matches = complete(line_buffer="d['before-af")
1119 self.assertIn("before-after", matches)
1129 self.assertIn("before-after", matches)
1120
1130
1121 # check completion on tuple-of-string keys at different stage - on first key
1131 # check completion on tuple-of-string keys at different stage - on first key
1122 ip.user_ns["d"] = {('foo', 'bar'): None}
1132 ip.user_ns["d"] = {('foo', 'bar'): None}
1123 _, matches = complete(line_buffer="d[")
1133 _, matches = complete(line_buffer="d[")
1124 self.assertIn("'foo'", matches)
1134 self.assertIn("'foo'", matches)
1125 self.assertNotIn("'foo']", matches)
1135 self.assertNotIn("'foo']", matches)
1126 self.assertNotIn("'bar'", matches)
1136 self.assertNotIn("'bar'", matches)
1127 self.assertNotIn("foo", matches)
1137 self.assertNotIn("foo", matches)
1128 self.assertNotIn("bar", matches)
1138 self.assertNotIn("bar", matches)
1129
1139
1130 # - match the prefix
1140 # - match the prefix
1131 _, matches = complete(line_buffer="d['f")
1141 _, matches = complete(line_buffer="d['f")
1132 self.assertIn("foo", matches)
1142 self.assertIn("foo", matches)
1133 self.assertNotIn("foo']", matches)
1143 self.assertNotIn("foo']", matches)
1134 self.assertNotIn('foo"]', matches)
1144 self.assertNotIn('foo"]', matches)
1135 _, matches = complete(line_buffer="d['foo")
1145 _, matches = complete(line_buffer="d['foo")
1136 self.assertIn("foo", matches)
1146 self.assertIn("foo", matches)
1137
1147
1138 # - can complete on second key
1148 # - can complete on second key
1139 _, matches = complete(line_buffer="d['foo', ")
1149 _, matches = complete(line_buffer="d['foo', ")
1140 self.assertIn("'bar'", matches)
1150 self.assertIn("'bar'", matches)
1141 _, matches = complete(line_buffer="d['foo', 'b")
1151 _, matches = complete(line_buffer="d['foo', 'b")
1142 self.assertIn("bar", matches)
1152 self.assertIn("bar", matches)
1143 self.assertNotIn("foo", matches)
1153 self.assertNotIn("foo", matches)
1144
1154
1145 # - does not propose missing keys
1155 # - does not propose missing keys
1146 _, matches = complete(line_buffer="d['foo', 'f")
1156 _, matches = complete(line_buffer="d['foo', 'f")
1147 self.assertNotIn("bar", matches)
1157 self.assertNotIn("bar", matches)
1148 self.assertNotIn("foo", matches)
1158 self.assertNotIn("foo", matches)
1149
1159
1150 # check sensitivity to following context
1160 # check sensitivity to following context
1151 _, matches = complete(line_buffer="d['foo',]", cursor_pos=8)
1161 _, matches = complete(line_buffer="d['foo',]", cursor_pos=8)
1152 self.assertIn("'bar'", matches)
1162 self.assertIn("'bar'", matches)
1153 self.assertNotIn("bar", matches)
1163 self.assertNotIn("bar", matches)
1154 self.assertNotIn("'foo'", matches)
1164 self.assertNotIn("'foo'", matches)
1155 self.assertNotIn("foo", matches)
1165 self.assertNotIn("foo", matches)
1156
1166
1157 _, matches = complete(line_buffer="d['']", cursor_pos=3)
1167 _, matches = complete(line_buffer="d['']", cursor_pos=3)
1158 self.assertIn("foo", matches)
1168 self.assertIn("foo", matches)
1159 assert not any(m.endswith(("]", '"', "'")) for m in matches), matches
1169 assert not any(m.endswith(("]", '"', "'")) for m in matches), matches
1160
1170
1161 _, matches = complete(line_buffer='d[""]', cursor_pos=3)
1171 _, matches = complete(line_buffer='d[""]', cursor_pos=3)
1162 self.assertIn("foo", matches)
1172 self.assertIn("foo", matches)
1163 assert not any(m.endswith(("]", '"', "'")) for m in matches), matches
1173 assert not any(m.endswith(("]", '"', "'")) for m in matches), matches
1164
1174
1165 _, matches = complete(line_buffer='d["foo","]', cursor_pos=9)
1175 _, matches = complete(line_buffer='d["foo","]', cursor_pos=9)
1166 self.assertIn("bar", matches)
1176 self.assertIn("bar", matches)
1167 assert not any(m.endswith(("]", '"', "'")) for m in matches), matches
1177 assert not any(m.endswith(("]", '"', "'")) for m in matches), matches
1168
1178
1169 _, matches = complete(line_buffer='d["foo",]', cursor_pos=8)
1179 _, matches = complete(line_buffer='d["foo",]', cursor_pos=8)
1170 self.assertIn("'bar'", matches)
1180 self.assertIn("'bar'", matches)
1171 self.assertNotIn("bar", matches)
1181 self.assertNotIn("bar", matches)
1172
1182
1173 # Can complete with longer tuple keys
1183 # Can complete with longer tuple keys
1174 ip.user_ns["d"] = {('foo', 'bar', 'foobar'): None}
1184 ip.user_ns["d"] = {('foo', 'bar', 'foobar'): None}
1175
1185
1176 # - can complete second key
1186 # - can complete second key
1177 _, matches = complete(line_buffer="d['foo', 'b")
1187 _, matches = complete(line_buffer="d['foo', 'b")
1178 self.assertIn("bar", matches)
1188 self.assertIn("bar", matches)
1179 self.assertNotIn("foo", matches)
1189 self.assertNotIn("foo", matches)
1180 self.assertNotIn("foobar", matches)
1190 self.assertNotIn("foobar", matches)
1181
1191
1182 # - can complete third key
1192 # - can complete third key
1183 _, matches = complete(line_buffer="d['foo', 'bar', 'fo")
1193 _, matches = complete(line_buffer="d['foo', 'bar', 'fo")
1184 self.assertIn("foobar", matches)
1194 self.assertIn("foobar", matches)
1185 self.assertNotIn("foo", matches)
1195 self.assertNotIn("foo", matches)
1186 self.assertNotIn("bar", matches)
1196 self.assertNotIn("bar", matches)
1187
1197
1188 def test_dict_key_completion_numbers(self):
1198 def test_dict_key_completion_numbers(self):
1189 ip = get_ipython()
1199 ip = get_ipython()
1190 complete = ip.Completer.complete
1200 complete = ip.Completer.complete
1191
1201
1192 ip.user_ns["d"] = {
1202 ip.user_ns["d"] = {
1193 0xDEADBEEF: None, # 3735928559
1203 0xDEADBEEF: None, # 3735928559
1194 1111: None,
1204 1111: None,
1195 1234: None,
1205 1234: None,
1196 "1999": None,
1206 "1999": None,
1197 0b10101: None, # 21
1207 0b10101: None, # 21
1198 22: None,
1208 22: None,
1199 }
1209 }
1200 _, matches = complete(line_buffer="d[1")
1210 _, matches = complete(line_buffer="d[1")
1201 self.assertIn("1111", matches)
1211 self.assertIn("1111", matches)
1202 self.assertIn("1234", matches)
1212 self.assertIn("1234", matches)
1203 self.assertNotIn("1999", matches)
1213 self.assertNotIn("1999", matches)
1204 self.assertNotIn("'1999'", matches)
1214 self.assertNotIn("'1999'", matches)
1205
1215
1206 _, matches = complete(line_buffer="d[0xdead")
1216 _, matches = complete(line_buffer="d[0xdead")
1207 self.assertIn("0xdeadbeef", matches)
1217 self.assertIn("0xdeadbeef", matches)
1208
1218
1209 _, matches = complete(line_buffer="d[2")
1219 _, matches = complete(line_buffer="d[2")
1210 self.assertIn("21", matches)
1220 self.assertIn("21", matches)
1211 self.assertIn("22", matches)
1221 self.assertIn("22", matches)
1212
1222
1213 _, matches = complete(line_buffer="d[0b101")
1223 _, matches = complete(line_buffer="d[0b101")
1214 self.assertIn("0b10101", matches)
1224 self.assertIn("0b10101", matches)
1215 self.assertIn("0b10110", matches)
1225 self.assertIn("0b10110", matches)
1216
1226
1217 def test_dict_key_completion_contexts(self):
1227 def test_dict_key_completion_contexts(self):
1218 """Test expression contexts in which dict key completion occurs"""
1228 """Test expression contexts in which dict key completion occurs"""
1219 ip = get_ipython()
1229 ip = get_ipython()
1220 complete = ip.Completer.complete
1230 complete = ip.Completer.complete
1221 d = {"abc": None}
1231 d = {"abc": None}
1222 ip.user_ns["d"] = d
1232 ip.user_ns["d"] = d
1223
1233
1224 class C:
1234 class C:
1225 data = d
1235 data = d
1226
1236
1227 ip.user_ns["C"] = C
1237 ip.user_ns["C"] = C
1228 ip.user_ns["get"] = lambda: d
1238 ip.user_ns["get"] = lambda: d
1229 ip.user_ns["nested"] = {"x": d}
1239 ip.user_ns["nested"] = {"x": d}
1230
1240
1231 def assert_no_completion(**kwargs):
1241 def assert_no_completion(**kwargs):
1232 _, matches = complete(**kwargs)
1242 _, matches = complete(**kwargs)
1233 self.assertNotIn("abc", matches)
1243 self.assertNotIn("abc", matches)
1234 self.assertNotIn("abc'", matches)
1244 self.assertNotIn("abc'", matches)
1235 self.assertNotIn("abc']", matches)
1245 self.assertNotIn("abc']", matches)
1236 self.assertNotIn("'abc'", matches)
1246 self.assertNotIn("'abc'", matches)
1237 self.assertNotIn("'abc']", matches)
1247 self.assertNotIn("'abc']", matches)
1238
1248
1239 def assert_completion(**kwargs):
1249 def assert_completion(**kwargs):
1240 _, matches = complete(**kwargs)
1250 _, matches = complete(**kwargs)
1241 self.assertIn("'abc'", matches)
1251 self.assertIn("'abc'", matches)
1242 self.assertNotIn("'abc']", matches)
1252 self.assertNotIn("'abc']", matches)
1243
1253
1244 # no completion after string closed, even if reopened
1254 # no completion after string closed, even if reopened
1245 assert_no_completion(line_buffer="d['a'")
1255 assert_no_completion(line_buffer="d['a'")
1246 assert_no_completion(line_buffer='d["a"')
1256 assert_no_completion(line_buffer='d["a"')
1247 assert_no_completion(line_buffer="d['a' + ")
1257 assert_no_completion(line_buffer="d['a' + ")
1248 assert_no_completion(line_buffer="d['a' + '")
1258 assert_no_completion(line_buffer="d['a' + '")
1249
1259
1250 # completion in non-trivial expressions
1260 # completion in non-trivial expressions
1251 assert_completion(line_buffer="+ d[")
1261 assert_completion(line_buffer="+ d[")
1252 assert_completion(line_buffer="(d[")
1262 assert_completion(line_buffer="(d[")
1253 assert_completion(line_buffer="C.data[")
1263 assert_completion(line_buffer="C.data[")
1254
1264
1255 # nested dict completion
1265 # nested dict completion
1256 assert_completion(line_buffer="nested['x'][")
1266 assert_completion(line_buffer="nested['x'][")
1257
1267
1258 with evaluation_policy("minimal"):
1268 with evaluation_policy("minimal"):
1259 with pytest.raises(AssertionError):
1269 with pytest.raises(AssertionError):
1260 assert_completion(line_buffer="nested['x'][")
1270 assert_completion(line_buffer="nested['x'][")
1261
1271
1262 # greedy flag
1272 # greedy flag
1263 def assert_completion(**kwargs):
1273 def assert_completion(**kwargs):
1264 _, matches = complete(**kwargs)
1274 _, matches = complete(**kwargs)
1265 self.assertIn("get()['abc']", matches)
1275 self.assertIn("get()['abc']", matches)
1266
1276
1267 assert_no_completion(line_buffer="get()[")
1277 assert_no_completion(line_buffer="get()[")
1268 with greedy_completion():
1278 with greedy_completion():
1269 assert_completion(line_buffer="get()[")
1279 assert_completion(line_buffer="get()[")
1270 assert_completion(line_buffer="get()['")
1280 assert_completion(line_buffer="get()['")
1271 assert_completion(line_buffer="get()['a")
1281 assert_completion(line_buffer="get()['a")
1272 assert_completion(line_buffer="get()['ab")
1282 assert_completion(line_buffer="get()['ab")
1273 assert_completion(line_buffer="get()['abc")
1283 assert_completion(line_buffer="get()['abc")
1274
1284
1275 def test_dict_key_completion_bytes(self):
1285 def test_dict_key_completion_bytes(self):
1276 """Test handling of bytes in dict key completion"""
1286 """Test handling of bytes in dict key completion"""
1277 ip = get_ipython()
1287 ip = get_ipython()
1278 complete = ip.Completer.complete
1288 complete = ip.Completer.complete
1279
1289
1280 ip.user_ns["d"] = {"abc": None, b"abd": None}
1290 ip.user_ns["d"] = {"abc": None, b"abd": None}
1281
1291
1282 _, matches = complete(line_buffer="d[")
1292 _, matches = complete(line_buffer="d[")
1283 self.assertIn("'abc'", matches)
1293 self.assertIn("'abc'", matches)
1284 self.assertIn("b'abd'", matches)
1294 self.assertIn("b'abd'", matches)
1285
1295
1286 if False: # not currently implemented
1296 if False: # not currently implemented
1287 _, matches = complete(line_buffer="d[b")
1297 _, matches = complete(line_buffer="d[b")
1288 self.assertIn("b'abd'", matches)
1298 self.assertIn("b'abd'", matches)
1289 self.assertNotIn("b'abc'", matches)
1299 self.assertNotIn("b'abc'", matches)
1290
1300
1291 _, matches = complete(line_buffer="d[b'")
1301 _, matches = complete(line_buffer="d[b'")
1292 self.assertIn("abd", matches)
1302 self.assertIn("abd", matches)
1293 self.assertNotIn("abc", matches)
1303 self.assertNotIn("abc", matches)
1294
1304
1295 _, matches = complete(line_buffer="d[B'")
1305 _, matches = complete(line_buffer="d[B'")
1296 self.assertIn("abd", matches)
1306 self.assertIn("abd", matches)
1297 self.assertNotIn("abc", matches)
1307 self.assertNotIn("abc", matches)
1298
1308
1299 _, matches = complete(line_buffer="d['")
1309 _, matches = complete(line_buffer="d['")
1300 self.assertIn("abc", matches)
1310 self.assertIn("abc", matches)
1301 self.assertNotIn("abd", matches)
1311 self.assertNotIn("abd", matches)
1302
1312
1303 def test_dict_key_completion_unicode_py3(self):
1313 def test_dict_key_completion_unicode_py3(self):
1304 """Test handling of unicode in dict key completion"""
1314 """Test handling of unicode in dict key completion"""
1305 ip = get_ipython()
1315 ip = get_ipython()
1306 complete = ip.Completer.complete
1316 complete = ip.Completer.complete
1307
1317
1308 ip.user_ns["d"] = {"a\u05d0": None}
1318 ip.user_ns["d"] = {"a\u05d0": None}
1309
1319
1310 # query using escape
1320 # query using escape
1311 if sys.platform != "win32":
1321 if sys.platform != "win32":
1312 # Known failure on Windows
1322 # Known failure on Windows
1313 _, matches = complete(line_buffer="d['a\\u05d0")
1323 _, matches = complete(line_buffer="d['a\\u05d0")
1314 self.assertIn("u05d0", matches) # tokenized after \\
1324 self.assertIn("u05d0", matches) # tokenized after \\
1315
1325
1316 # query using character
1326 # query using character
1317 _, matches = complete(line_buffer="d['a\u05d0")
1327 _, matches = complete(line_buffer="d['a\u05d0")
1318 self.assertIn("a\u05d0", matches)
1328 self.assertIn("a\u05d0", matches)
1319
1329
1320 with greedy_completion():
1330 with greedy_completion():
1321 # query using escape
1331 # query using escape
1322 _, matches = complete(line_buffer="d['a\\u05d0")
1332 _, matches = complete(line_buffer="d['a\\u05d0")
1323 self.assertIn("d['a\\u05d0']", matches) # tokenized after \\
1333 self.assertIn("d['a\\u05d0']", matches) # tokenized after \\
1324
1334
1325 # query using character
1335 # query using character
1326 _, matches = complete(line_buffer="d['a\u05d0")
1336 _, matches = complete(line_buffer="d['a\u05d0")
1327 self.assertIn("d['a\u05d0']", matches)
1337 self.assertIn("d['a\u05d0']", matches)
1328
1338
1329 @dec.skip_without("numpy")
1339 @dec.skip_without("numpy")
1330 def test_struct_array_key_completion(self):
1340 def test_struct_array_key_completion(self):
1331 """Test dict key completion applies to numpy struct arrays"""
1341 """Test dict key completion applies to numpy struct arrays"""
1332 import numpy
1342 import numpy
1333
1343
1334 ip = get_ipython()
1344 ip = get_ipython()
1335 complete = ip.Completer.complete
1345 complete = ip.Completer.complete
1336 ip.user_ns["d"] = numpy.array([], dtype=[("hello", "f"), ("world", "f")])
1346 ip.user_ns["d"] = numpy.array([], dtype=[("hello", "f"), ("world", "f")])
1337 _, matches = complete(line_buffer="d['")
1347 _, matches = complete(line_buffer="d['")
1338 self.assertIn("hello", matches)
1348 self.assertIn("hello", matches)
1339 self.assertIn("world", matches)
1349 self.assertIn("world", matches)
1340 # complete on the numpy struct itself
1350 # complete on the numpy struct itself
1341 dt = numpy.dtype(
1351 dt = numpy.dtype(
1342 [("my_head", [("my_dt", ">u4"), ("my_df", ">u4")]), ("my_data", ">f4", 5)]
1352 [("my_head", [("my_dt", ">u4"), ("my_df", ">u4")]), ("my_data", ">f4", 5)]
1343 )
1353 )
1344 x = numpy.zeros(2, dtype=dt)
1354 x = numpy.zeros(2, dtype=dt)
1345 ip.user_ns["d"] = x[1]
1355 ip.user_ns["d"] = x[1]
1346 _, matches = complete(line_buffer="d['")
1356 _, matches = complete(line_buffer="d['")
1347 self.assertIn("my_head", matches)
1357 self.assertIn("my_head", matches)
1348 self.assertIn("my_data", matches)
1358 self.assertIn("my_data", matches)
1349
1359
1350 def completes_on_nested():
1360 def completes_on_nested():
1351 ip.user_ns["d"] = numpy.zeros(2, dtype=dt)
1361 ip.user_ns["d"] = numpy.zeros(2, dtype=dt)
1352 _, matches = complete(line_buffer="d[1]['my_head']['")
1362 _, matches = complete(line_buffer="d[1]['my_head']['")
1353 self.assertTrue(any(["my_dt" in m for m in matches]))
1363 self.assertTrue(any(["my_dt" in m for m in matches]))
1354 self.assertTrue(any(["my_df" in m for m in matches]))
1364 self.assertTrue(any(["my_df" in m for m in matches]))
1355 # complete on a nested level
1365 # complete on a nested level
1356 with greedy_completion():
1366 with greedy_completion():
1357 completes_on_nested()
1367 completes_on_nested()
1358
1368
1359 with evaluation_policy("limited"):
1369 with evaluation_policy("limited"):
1360 completes_on_nested()
1370 completes_on_nested()
1361
1371
1362 with evaluation_policy("minimal"):
1372 with evaluation_policy("minimal"):
1363 with pytest.raises(AssertionError):
1373 with pytest.raises(AssertionError):
1364 completes_on_nested()
1374 completes_on_nested()
1365
1375
1366 @dec.skip_without("pandas")
1376 @dec.skip_without("pandas")
1367 def test_dataframe_key_completion(self):
1377 def test_dataframe_key_completion(self):
1368 """Test dict key completion applies to pandas DataFrames"""
1378 """Test dict key completion applies to pandas DataFrames"""
1369 import pandas
1379 import pandas
1370
1380
1371 ip = get_ipython()
1381 ip = get_ipython()
1372 complete = ip.Completer.complete
1382 complete = ip.Completer.complete
1373 ip.user_ns["d"] = pandas.DataFrame({"hello": [1], "world": [2]})
1383 ip.user_ns["d"] = pandas.DataFrame({"hello": [1], "world": [2]})
1374 _, matches = complete(line_buffer="d['")
1384 _, matches = complete(line_buffer="d['")
1375 self.assertIn("hello", matches)
1385 self.assertIn("hello", matches)
1376 self.assertIn("world", matches)
1386 self.assertIn("world", matches)
1377 _, matches = complete(line_buffer="d.loc[:, '")
1387 _, matches = complete(line_buffer="d.loc[:, '")
1378 self.assertIn("hello", matches)
1388 self.assertIn("hello", matches)
1379 self.assertIn("world", matches)
1389 self.assertIn("world", matches)
1380 _, matches = complete(line_buffer="d.loc[1:, '")
1390 _, matches = complete(line_buffer="d.loc[1:, '")
1381 self.assertIn("hello", matches)
1391 self.assertIn("hello", matches)
1382 _, matches = complete(line_buffer="d.loc[1:1, '")
1392 _, matches = complete(line_buffer="d.loc[1:1, '")
1383 self.assertIn("hello", matches)
1393 self.assertIn("hello", matches)
1384 _, matches = complete(line_buffer="d.loc[1:1:-1, '")
1394 _, matches = complete(line_buffer="d.loc[1:1:-1, '")
1385 self.assertIn("hello", matches)
1395 self.assertIn("hello", matches)
1386 _, matches = complete(line_buffer="d.loc[::, '")
1396 _, matches = complete(line_buffer="d.loc[::, '")
1387 self.assertIn("hello", matches)
1397 self.assertIn("hello", matches)
1388
1398
1389 def test_dict_key_completion_invalids(self):
1399 def test_dict_key_completion_invalids(self):
1390 """Smoke test cases dict key completion can't handle"""
1400 """Smoke test cases dict key completion can't handle"""
1391 ip = get_ipython()
1401 ip = get_ipython()
1392 complete = ip.Completer.complete
1402 complete = ip.Completer.complete
1393
1403
1394 ip.user_ns["no_getitem"] = None
1404 ip.user_ns["no_getitem"] = None
1395 ip.user_ns["no_keys"] = []
1405 ip.user_ns["no_keys"] = []
1396 ip.user_ns["cant_call_keys"] = dict
1406 ip.user_ns["cant_call_keys"] = dict
1397 ip.user_ns["empty"] = {}
1407 ip.user_ns["empty"] = {}
1398 ip.user_ns["d"] = {"abc": 5}
1408 ip.user_ns["d"] = {"abc": 5}
1399
1409
1400 _, matches = complete(line_buffer="no_getitem['")
1410 _, matches = complete(line_buffer="no_getitem['")
1401 _, matches = complete(line_buffer="no_keys['")
1411 _, matches = complete(line_buffer="no_keys['")
1402 _, matches = complete(line_buffer="cant_call_keys['")
1412 _, matches = complete(line_buffer="cant_call_keys['")
1403 _, matches = complete(line_buffer="empty['")
1413 _, matches = complete(line_buffer="empty['")
1404 _, matches = complete(line_buffer="name_error['")
1414 _, matches = complete(line_buffer="name_error['")
1405 _, matches = complete(line_buffer="d['\\") # incomplete escape
1415 _, matches = complete(line_buffer="d['\\") # incomplete escape
1406
1416
1407 def test_object_key_completion(self):
1417 def test_object_key_completion(self):
1408 ip = get_ipython()
1418 ip = get_ipython()
1409 ip.user_ns["key_completable"] = KeyCompletable(["qwerty", "qwick"])
1419 ip.user_ns["key_completable"] = KeyCompletable(["qwerty", "qwick"])
1410
1420
1411 _, matches = ip.Completer.complete(line_buffer="key_completable['qw")
1421 _, matches = ip.Completer.complete(line_buffer="key_completable['qw")
1412 self.assertIn("qwerty", matches)
1422 self.assertIn("qwerty", matches)
1413 self.assertIn("qwick", matches)
1423 self.assertIn("qwick", matches)
1414
1424
1415 def test_class_key_completion(self):
1425 def test_class_key_completion(self):
1416 ip = get_ipython()
1426 ip = get_ipython()
1417 NamedInstanceClass("qwerty")
1427 NamedInstanceClass("qwerty")
1418 NamedInstanceClass("qwick")
1428 NamedInstanceClass("qwick")
1419 ip.user_ns["named_instance_class"] = NamedInstanceClass
1429 ip.user_ns["named_instance_class"] = NamedInstanceClass
1420
1430
1421 _, matches = ip.Completer.complete(line_buffer="named_instance_class['qw")
1431 _, matches = ip.Completer.complete(line_buffer="named_instance_class['qw")
1422 self.assertIn("qwerty", matches)
1432 self.assertIn("qwerty", matches)
1423 self.assertIn("qwick", matches)
1433 self.assertIn("qwick", matches)
1424
1434
1425 def test_tryimport(self):
1435 def test_tryimport(self):
1426 """
1436 """
1427 Test that try-import don't crash on trailing dot, and import modules before
1437 Test that try-import don't crash on trailing dot, and import modules before
1428 """
1438 """
1429 from IPython.core.completerlib import try_import
1439 from IPython.core.completerlib import try_import
1430
1440
1431 assert try_import("IPython.")
1441 assert try_import("IPython.")
1432
1442
1433 def test_aimport_module_completer(self):
1443 def test_aimport_module_completer(self):
1434 ip = get_ipython()
1444 ip = get_ipython()
1435 _, matches = ip.complete("i", "%aimport i")
1445 _, matches = ip.complete("i", "%aimport i")
1436 self.assertIn("io", matches)
1446 self.assertIn("io", matches)
1437 self.assertNotIn("int", matches)
1447 self.assertNotIn("int", matches)
1438
1448
1439 def test_nested_import_module_completer(self):
1449 def test_nested_import_module_completer(self):
1440 ip = get_ipython()
1450 ip = get_ipython()
1441 _, matches = ip.complete(None, "import IPython.co", 17)
1451 _, matches = ip.complete(None, "import IPython.co", 17)
1442 self.assertIn("IPython.core", matches)
1452 self.assertIn("IPython.core", matches)
1443 self.assertNotIn("import IPython.core", matches)
1453 self.assertNotIn("import IPython.core", matches)
1444 self.assertNotIn("IPython.display", matches)
1454 self.assertNotIn("IPython.display", matches)
1445
1455
1446 def test_import_module_completer(self):
1456 def test_import_module_completer(self):
1447 ip = get_ipython()
1457 ip = get_ipython()
1448 _, matches = ip.complete("i", "import i")
1458 _, matches = ip.complete("i", "import i")
1449 self.assertIn("io", matches)
1459 self.assertIn("io", matches)
1450 self.assertNotIn("int", matches)
1460 self.assertNotIn("int", matches)
1451
1461
1452 def test_from_module_completer(self):
1462 def test_from_module_completer(self):
1453 ip = get_ipython()
1463 ip = get_ipython()
1454 _, matches = ip.complete("B", "from io import B", 16)
1464 _, matches = ip.complete("B", "from io import B", 16)
1455 self.assertIn("BytesIO", matches)
1465 self.assertIn("BytesIO", matches)
1456 self.assertNotIn("BaseException", matches)
1466 self.assertNotIn("BaseException", matches)
1457
1467
1458 def test_snake_case_completion(self):
1468 def test_snake_case_completion(self):
1459 ip = get_ipython()
1469 ip = get_ipython()
1460 ip.Completer.use_jedi = False
1470 ip.Completer.use_jedi = False
1461 ip.user_ns["some_three"] = 3
1471 ip.user_ns["some_three"] = 3
1462 ip.user_ns["some_four"] = 4
1472 ip.user_ns["some_four"] = 4
1463 _, matches = ip.complete("s_", "print(s_f")
1473 _, matches = ip.complete("s_", "print(s_f")
1464 self.assertIn("some_three", matches)
1474 self.assertIn("some_three", matches)
1465 self.assertIn("some_four", matches)
1475 self.assertIn("some_four", matches)
1466
1476
1467 def test_mix_terms(self):
1477 def test_mix_terms(self):
1468 ip = get_ipython()
1478 ip = get_ipython()
1469 from textwrap import dedent
1479 from textwrap import dedent
1470
1480
1471 ip.Completer.use_jedi = False
1481 ip.Completer.use_jedi = False
1472 ip.ex(
1482 ip.ex(
1473 dedent(
1483 dedent(
1474 """
1484 """
1475 class Test:
1485 class Test:
1476 def meth(self, meth_arg1):
1486 def meth(self, meth_arg1):
1477 print("meth")
1487 print("meth")
1478
1488
1479 def meth_1(self, meth1_arg1, meth1_arg2):
1489 def meth_1(self, meth1_arg1, meth1_arg2):
1480 print("meth1")
1490 print("meth1")
1481
1491
1482 def meth_2(self, meth2_arg1, meth2_arg2):
1492 def meth_2(self, meth2_arg1, meth2_arg2):
1483 print("meth2")
1493 print("meth2")
1484 test = Test()
1494 test = Test()
1485 """
1495 """
1486 )
1496 )
1487 )
1497 )
1488 _, matches = ip.complete(None, "test.meth(")
1498 _, matches = ip.complete(None, "test.meth(")
1489 self.assertIn("meth_arg1=", matches)
1499 self.assertIn("meth_arg1=", matches)
1490 self.assertNotIn("meth2_arg1=", matches)
1500 self.assertNotIn("meth2_arg1=", matches)
1491
1501
1492 def test_percent_symbol_restrict_to_magic_completions(self):
1502 def test_percent_symbol_restrict_to_magic_completions(self):
1493 ip = get_ipython()
1503 ip = get_ipython()
1494 completer = ip.Completer
1504 completer = ip.Completer
1495 text = "%a"
1505 text = "%a"
1496
1506
1497 with provisionalcompleter():
1507 with provisionalcompleter():
1498 completer.use_jedi = True
1508 completer.use_jedi = True
1499 completions = completer.completions(text, len(text))
1509 completions = completer.completions(text, len(text))
1500 for c in completions:
1510 for c in completions:
1501 self.assertEqual(c.text[0], "%")
1511 self.assertEqual(c.text[0], "%")
1502
1512
1503 def test_fwd_unicode_restricts(self):
1513 def test_fwd_unicode_restricts(self):
1504 ip = get_ipython()
1514 ip = get_ipython()
1505 completer = ip.Completer
1515 completer = ip.Completer
1506 text = "\\ROMAN NUMERAL FIVE"
1516 text = "\\ROMAN NUMERAL FIVE"
1507
1517
1508 with provisionalcompleter():
1518 with provisionalcompleter():
1509 completer.use_jedi = True
1519 completer.use_jedi = True
1510 completions = [
1520 completions = [
1511 completion.text for completion in completer.completions(text, len(text))
1521 completion.text for completion in completer.completions(text, len(text))
1512 ]
1522 ]
1513 self.assertEqual(completions, ["\u2164"])
1523 self.assertEqual(completions, ["\u2164"])
1514
1524
1515 def test_dict_key_restrict_to_dicts(self):
1525 def test_dict_key_restrict_to_dicts(self):
1516 """Test that dict key suppresses non-dict completion items"""
1526 """Test that dict key suppresses non-dict completion items"""
1517 ip = get_ipython()
1527 ip = get_ipython()
1518 c = ip.Completer
1528 c = ip.Completer
1519 d = {"abc": None}
1529 d = {"abc": None}
1520 ip.user_ns["d"] = d
1530 ip.user_ns["d"] = d
1521
1531
1522 text = 'd["a'
1532 text = 'd["a'
1523
1533
1524 def _():
1534 def _():
1525 with provisionalcompleter():
1535 with provisionalcompleter():
1526 c.use_jedi = True
1536 c.use_jedi = True
1527 return [
1537 return [
1528 completion.text for completion in c.completions(text, len(text))
1538 completion.text for completion in c.completions(text, len(text))
1529 ]
1539 ]
1530
1540
1531 completions = _()
1541 completions = _()
1532 self.assertEqual(completions, ["abc"])
1542 self.assertEqual(completions, ["abc"])
1533
1543
1534 # check that it can be disabled in granular manner:
1544 # check that it can be disabled in granular manner:
1535 cfg = Config()
1545 cfg = Config()
1536 cfg.IPCompleter.suppress_competing_matchers = {
1546 cfg.IPCompleter.suppress_competing_matchers = {
1537 "IPCompleter.dict_key_matcher": False
1547 "IPCompleter.dict_key_matcher": False
1538 }
1548 }
1539 c.update_config(cfg)
1549 c.update_config(cfg)
1540
1550
1541 completions = _()
1551 completions = _()
1542 self.assertIn("abc", completions)
1552 self.assertIn("abc", completions)
1543 self.assertGreater(len(completions), 1)
1553 self.assertGreater(len(completions), 1)
1544
1554
1545 def test_matcher_suppression(self):
1555 def test_matcher_suppression(self):
1546 @completion_matcher(identifier="a_matcher")
1556 @completion_matcher(identifier="a_matcher")
1547 def a_matcher(text):
1557 def a_matcher(text):
1548 return ["completion_a"]
1558 return ["completion_a"]
1549
1559
1550 @completion_matcher(identifier="b_matcher", api_version=2)
1560 @completion_matcher(identifier="b_matcher", api_version=2)
1551 def b_matcher(context: CompletionContext):
1561 def b_matcher(context: CompletionContext):
1552 text = context.token
1562 text = context.token
1553 result = {"completions": [SimpleCompletion("completion_b")]}
1563 result = {"completions": [SimpleCompletion("completion_b")]}
1554
1564
1555 if text == "suppress c":
1565 if text == "suppress c":
1556 result["suppress"] = {"c_matcher"}
1566 result["suppress"] = {"c_matcher"}
1557
1567
1558 if text.startswith("suppress all"):
1568 if text.startswith("suppress all"):
1559 result["suppress"] = True
1569 result["suppress"] = True
1560 if text == "suppress all but c":
1570 if text == "suppress all but c":
1561 result["do_not_suppress"] = {"c_matcher"}
1571 result["do_not_suppress"] = {"c_matcher"}
1562 if text == "suppress all but a":
1572 if text == "suppress all but a":
1563 result["do_not_suppress"] = {"a_matcher"}
1573 result["do_not_suppress"] = {"a_matcher"}
1564
1574
1565 return result
1575 return result
1566
1576
1567 @completion_matcher(identifier="c_matcher")
1577 @completion_matcher(identifier="c_matcher")
1568 def c_matcher(text):
1578 def c_matcher(text):
1569 return ["completion_c"]
1579 return ["completion_c"]
1570
1580
1571 with custom_matchers([a_matcher, b_matcher, c_matcher]):
1581 with custom_matchers([a_matcher, b_matcher, c_matcher]):
1572 ip = get_ipython()
1582 ip = get_ipython()
1573 c = ip.Completer
1583 c = ip.Completer
1574
1584
1575 def _(text, expected):
1585 def _(text, expected):
1576 c.use_jedi = False
1586 c.use_jedi = False
1577 s, matches = c.complete(text)
1587 s, matches = c.complete(text)
1578 self.assertEqual(expected, matches)
1588 self.assertEqual(expected, matches)
1579
1589
1580 _("do not suppress", ["completion_a", "completion_b", "completion_c"])
1590 _("do not suppress", ["completion_a", "completion_b", "completion_c"])
1581 _("suppress all", ["completion_b"])
1591 _("suppress all", ["completion_b"])
1582 _("suppress all but a", ["completion_a", "completion_b"])
1592 _("suppress all but a", ["completion_a", "completion_b"])
1583 _("suppress all but c", ["completion_b", "completion_c"])
1593 _("suppress all but c", ["completion_b", "completion_c"])
1584
1594
1585 def configure(suppression_config):
1595 def configure(suppression_config):
1586 cfg = Config()
1596 cfg = Config()
1587 cfg.IPCompleter.suppress_competing_matchers = suppression_config
1597 cfg.IPCompleter.suppress_competing_matchers = suppression_config
1588 c.update_config(cfg)
1598 c.update_config(cfg)
1589
1599
1590 # test that configuration takes priority over the run-time decisions
1600 # test that configuration takes priority over the run-time decisions
1591
1601
1592 configure(False)
1602 configure(False)
1593 _("suppress all", ["completion_a", "completion_b", "completion_c"])
1603 _("suppress all", ["completion_a", "completion_b", "completion_c"])
1594
1604
1595 configure({"b_matcher": False})
1605 configure({"b_matcher": False})
1596 _("suppress all", ["completion_a", "completion_b", "completion_c"])
1606 _("suppress all", ["completion_a", "completion_b", "completion_c"])
1597
1607
1598 configure({"a_matcher": False})
1608 configure({"a_matcher": False})
1599 _("suppress all", ["completion_b"])
1609 _("suppress all", ["completion_b"])
1600
1610
1601 configure({"b_matcher": True})
1611 configure({"b_matcher": True})
1602 _("do not suppress", ["completion_b"])
1612 _("do not suppress", ["completion_b"])
1603
1613
1604 configure(True)
1614 configure(True)
1605 _("do not suppress", ["completion_a"])
1615 _("do not suppress", ["completion_a"])
1606
1616
1607 def test_matcher_suppression_with_iterator(self):
1617 def test_matcher_suppression_with_iterator(self):
1608 @completion_matcher(identifier="matcher_returning_iterator")
1618 @completion_matcher(identifier="matcher_returning_iterator")
1609 def matcher_returning_iterator(text):
1619 def matcher_returning_iterator(text):
1610 return iter(["completion_iter"])
1620 return iter(["completion_iter"])
1611
1621
1612 @completion_matcher(identifier="matcher_returning_list")
1622 @completion_matcher(identifier="matcher_returning_list")
1613 def matcher_returning_list(text):
1623 def matcher_returning_list(text):
1614 return ["completion_list"]
1624 return ["completion_list"]
1615
1625
1616 with custom_matchers([matcher_returning_iterator, matcher_returning_list]):
1626 with custom_matchers([matcher_returning_iterator, matcher_returning_list]):
1617 ip = get_ipython()
1627 ip = get_ipython()
1618 c = ip.Completer
1628 c = ip.Completer
1619
1629
1620 def _(text, expected):
1630 def _(text, expected):
1621 c.use_jedi = False
1631 c.use_jedi = False
1622 s, matches = c.complete(text)
1632 s, matches = c.complete(text)
1623 self.assertEqual(expected, matches)
1633 self.assertEqual(expected, matches)
1624
1634
1625 def configure(suppression_config):
1635 def configure(suppression_config):
1626 cfg = Config()
1636 cfg = Config()
1627 cfg.IPCompleter.suppress_competing_matchers = suppression_config
1637 cfg.IPCompleter.suppress_competing_matchers = suppression_config
1628 c.update_config(cfg)
1638 c.update_config(cfg)
1629
1639
1630 configure(False)
1640 configure(False)
1631 _("---", ["completion_iter", "completion_list"])
1641 _("---", ["completion_iter", "completion_list"])
1632
1642
1633 configure(True)
1643 configure(True)
1634 _("---", ["completion_iter"])
1644 _("---", ["completion_iter"])
1635
1645
1636 configure(None)
1646 configure(None)
1637 _("--", ["completion_iter", "completion_list"])
1647 _("--", ["completion_iter", "completion_list"])
1638
1648
1639 @pytest.mark.xfail(
1649 @pytest.mark.xfail(
1640 sys.version_info.releaselevel in ("alpha",),
1650 sys.version_info.releaselevel in ("alpha",),
1641 reason="Parso does not yet parse 3.13",
1651 reason="Parso does not yet parse 3.13",
1642 )
1652 )
1643 def test_matcher_suppression_with_jedi(self):
1653 def test_matcher_suppression_with_jedi(self):
1644 ip = get_ipython()
1654 ip = get_ipython()
1645 c = ip.Completer
1655 c = ip.Completer
1646 c.use_jedi = True
1656 c.use_jedi = True
1647
1657
1648 def configure(suppression_config):
1658 def configure(suppression_config):
1649 cfg = Config()
1659 cfg = Config()
1650 cfg.IPCompleter.suppress_competing_matchers = suppression_config
1660 cfg.IPCompleter.suppress_competing_matchers = suppression_config
1651 c.update_config(cfg)
1661 c.update_config(cfg)
1652
1662
1653 def _():
1663 def _():
1654 with provisionalcompleter():
1664 with provisionalcompleter():
1655 matches = [completion.text for completion in c.completions("dict.", 5)]
1665 matches = [completion.text for completion in c.completions("dict.", 5)]
1656 self.assertIn("keys", matches)
1666 self.assertIn("keys", matches)
1657
1667
1658 configure(False)
1668 configure(False)
1659 _()
1669 _()
1660
1670
1661 configure(True)
1671 configure(True)
1662 _()
1672 _()
1663
1673
1664 configure(None)
1674 configure(None)
1665 _()
1675 _()
1666
1676
1667 def test_matcher_disabling(self):
1677 def test_matcher_disabling(self):
1668 @completion_matcher(identifier="a_matcher")
1678 @completion_matcher(identifier="a_matcher")
1669 def a_matcher(text):
1679 def a_matcher(text):
1670 return ["completion_a"]
1680 return ["completion_a"]
1671
1681
1672 @completion_matcher(identifier="b_matcher")
1682 @completion_matcher(identifier="b_matcher")
1673 def b_matcher(text):
1683 def b_matcher(text):
1674 return ["completion_b"]
1684 return ["completion_b"]
1675
1685
1676 def _(expected):
1686 def _(expected):
1677 s, matches = c.complete("completion_")
1687 s, matches = c.complete("completion_")
1678 self.assertEqual(expected, matches)
1688 self.assertEqual(expected, matches)
1679
1689
1680 with custom_matchers([a_matcher, b_matcher]):
1690 with custom_matchers([a_matcher, b_matcher]):
1681 ip = get_ipython()
1691 ip = get_ipython()
1682 c = ip.Completer
1692 c = ip.Completer
1683
1693
1684 _(["completion_a", "completion_b"])
1694 _(["completion_a", "completion_b"])
1685
1695
1686 cfg = Config()
1696 cfg = Config()
1687 cfg.IPCompleter.disable_matchers = ["b_matcher"]
1697 cfg.IPCompleter.disable_matchers = ["b_matcher"]
1688 c.update_config(cfg)
1698 c.update_config(cfg)
1689
1699
1690 _(["completion_a"])
1700 _(["completion_a"])
1691
1701
1692 cfg.IPCompleter.disable_matchers = []
1702 cfg.IPCompleter.disable_matchers = []
1693 c.update_config(cfg)
1703 c.update_config(cfg)
1694
1704
1695 def test_matcher_priority(self):
1705 def test_matcher_priority(self):
1696 @completion_matcher(identifier="a_matcher", priority=0, api_version=2)
1706 @completion_matcher(identifier="a_matcher", priority=0, api_version=2)
1697 def a_matcher(text):
1707 def a_matcher(text):
1698 return {"completions": [SimpleCompletion("completion_a")], "suppress": True}
1708 return {"completions": [SimpleCompletion("completion_a")], "suppress": True}
1699
1709
1700 @completion_matcher(identifier="b_matcher", priority=2, api_version=2)
1710 @completion_matcher(identifier="b_matcher", priority=2, api_version=2)
1701 def b_matcher(text):
1711 def b_matcher(text):
1702 return {"completions": [SimpleCompletion("completion_b")], "suppress": True}
1712 return {"completions": [SimpleCompletion("completion_b")], "suppress": True}
1703
1713
1704 def _(expected):
1714 def _(expected):
1705 s, matches = c.complete("completion_")
1715 s, matches = c.complete("completion_")
1706 self.assertEqual(expected, matches)
1716 self.assertEqual(expected, matches)
1707
1717
1708 with custom_matchers([a_matcher, b_matcher]):
1718 with custom_matchers([a_matcher, b_matcher]):
1709 ip = get_ipython()
1719 ip = get_ipython()
1710 c = ip.Completer
1720 c = ip.Completer
1711
1721
1712 _(["completion_b"])
1722 _(["completion_b"])
1713 a_matcher.matcher_priority = 3
1723 a_matcher.matcher_priority = 3
1714 _(["completion_a"])
1724 _(["completion_a"])
1715
1725
1716
1726
1717 @pytest.mark.parametrize(
1727 @pytest.mark.parametrize(
1718 "input, expected",
1728 "input, expected",
1719 [
1729 [
1720 ["1.234", "1.234"],
1730 ["1.234", "1.234"],
1721 # should match signed numbers
1731 # should match signed numbers
1722 ["+1", "+1"],
1732 ["+1", "+1"],
1723 ["-1", "-1"],
1733 ["-1", "-1"],
1724 ["-1.0", "-1.0"],
1734 ["-1.0", "-1.0"],
1725 ["-1.", "-1."],
1735 ["-1.", "-1."],
1726 ["+1.", "+1."],
1736 ["+1.", "+1."],
1727 [".1", ".1"],
1737 [".1", ".1"],
1728 # should not match non-numbers
1738 # should not match non-numbers
1729 ["1..", None],
1739 ["1..", None],
1730 ["..", None],
1740 ["..", None],
1731 [".1.", None],
1741 [".1.", None],
1732 # should match after comma
1742 # should match after comma
1733 [",1", "1"],
1743 [",1", "1"],
1734 [", 1", "1"],
1744 [", 1", "1"],
1735 [", .1", ".1"],
1745 [", .1", ".1"],
1736 [", +.1", "+.1"],
1746 [", +.1", "+.1"],
1737 # should not match after trailing spaces
1747 # should not match after trailing spaces
1738 [".1 ", None],
1748 [".1 ", None],
1739 # some complex cases
1749 # some complex cases
1740 ["0b_0011_1111_0100_1110", "0b_0011_1111_0100_1110"],
1750 ["0b_0011_1111_0100_1110", "0b_0011_1111_0100_1110"],
1741 ["0xdeadbeef", "0xdeadbeef"],
1751 ["0xdeadbeef", "0xdeadbeef"],
1742 ["0b_1110_0101", "0b_1110_0101"],
1752 ["0b_1110_0101", "0b_1110_0101"],
1743 # should not match if in an operation
1753 # should not match if in an operation
1744 ["1 + 1", None],
1754 ["1 + 1", None],
1745 [", 1 + 1", None],
1755 [", 1 + 1", None],
1746 ],
1756 ],
1747 )
1757 )
1748 def test_match_numeric_literal_for_dict_key(input, expected):
1758 def test_match_numeric_literal_for_dict_key(input, expected):
1749 assert _match_number_in_dict_key_prefix(input) == expected
1759 assert _match_number_in_dict_key_prefix(input) == expected
@@ -1,574 +1,579
1 """Tests for debugging machinery.
1 """Tests for debugging machinery.
2 """
2 """
3
3
4 # Copyright (c) IPython Development Team.
4 # Copyright (c) IPython Development Team.
5 # Distributed under the terms of the Modified BSD License.
5 # Distributed under the terms of the Modified BSD License.
6
6
7 import builtins
7 import builtins
8 import os
8 import os
9 import sys
9 import sys
10 import platform
10 import platform
11
11
12 from tempfile import NamedTemporaryFile
12 from tempfile import NamedTemporaryFile
13 from textwrap import dedent
13 from textwrap import dedent
14 from unittest.mock import patch
14 from unittest.mock import patch
15
15
16 from IPython.core import debugger
16 from IPython.core import debugger
17 from IPython.testing import IPYTHON_TESTING_TIMEOUT_SCALE
17 from IPython.testing import IPYTHON_TESTING_TIMEOUT_SCALE
18 from IPython.testing.decorators import skip_win32
18 from IPython.testing.decorators import skip_win32
19 import pytest
19 import pytest
20
20
21 #-----------------------------------------------------------------------------
21 #-----------------------------------------------------------------------------
22 # Helper classes, from CPython's Pdb test suite
22 # Helper classes, from CPython's Pdb test suite
23 #-----------------------------------------------------------------------------
23 #-----------------------------------------------------------------------------
24
24
25 class _FakeInput(object):
25 class _FakeInput(object):
26 """
26 """
27 A fake input stream for pdb's interactive debugger. Whenever a
27 A fake input stream for pdb's interactive debugger. Whenever a
28 line is read, print it (to simulate the user typing it), and then
28 line is read, print it (to simulate the user typing it), and then
29 return it. The set of lines to return is specified in the
29 return it. The set of lines to return is specified in the
30 constructor; they should not have trailing newlines.
30 constructor; they should not have trailing newlines.
31 """
31 """
32 def __init__(self, lines):
32 def __init__(self, lines):
33 self.lines = iter(lines)
33 self.lines = iter(lines)
34
34
35 def readline(self):
35 def readline(self):
36 line = next(self.lines)
36 line = next(self.lines)
37 print(line)
37 print(line)
38 return line+'\n'
38 return line+'\n'
39
39
40 class PdbTestInput(object):
40 class PdbTestInput(object):
41 """Context manager that makes testing Pdb in doctests easier."""
41 """Context manager that makes testing Pdb in doctests easier."""
42
42
43 def __init__(self, input):
43 def __init__(self, input):
44 self.input = input
44 self.input = input
45
45
46 def __enter__(self):
46 def __enter__(self):
47 self.real_stdin = sys.stdin
47 self.real_stdin = sys.stdin
48 sys.stdin = _FakeInput(self.input)
48 sys.stdin = _FakeInput(self.input)
49
49
50 def __exit__(self, *exc):
50 def __exit__(self, *exc):
51 sys.stdin = self.real_stdin
51 sys.stdin = self.real_stdin
52
52
53 #-----------------------------------------------------------------------------
53 #-----------------------------------------------------------------------------
54 # Tests
54 # Tests
55 #-----------------------------------------------------------------------------
55 #-----------------------------------------------------------------------------
56
56
57 def test_ipdb_magics():
57 def test_ipdb_magics():
58 '''Test calling some IPython magics from ipdb.
58 '''Test calling some IPython magics from ipdb.
59
59
60 First, set up some test functions and classes which we can inspect.
60 First, set up some test functions and classes which we can inspect.
61
61
62 >>> class ExampleClass(object):
62 >>> class ExampleClass(object):
63 ... """Docstring for ExampleClass."""
63 ... """Docstring for ExampleClass."""
64 ... def __init__(self):
64 ... def __init__(self):
65 ... """Docstring for ExampleClass.__init__"""
65 ... """Docstring for ExampleClass.__init__"""
66 ... pass
66 ... pass
67 ... def __str__(self):
67 ... def __str__(self):
68 ... return "ExampleClass()"
68 ... return "ExampleClass()"
69
69
70 >>> def example_function(x, y, z="hello"):
70 >>> def example_function(x, y, z="hello"):
71 ... """Docstring for example_function."""
71 ... """Docstring for example_function."""
72 ... pass
72 ... pass
73
73
74 >>> old_trace = sys.gettrace()
74 >>> old_trace = sys.gettrace()
75
75
76 Create a function which triggers ipdb.
76 Create a function which triggers ipdb.
77
77
78 >>> def trigger_ipdb():
78 >>> def trigger_ipdb():
79 ... a = ExampleClass()
79 ... a = ExampleClass()
80 ... debugger.Pdb().set_trace()
80 ... debugger.Pdb().set_trace()
81
81
82 >>> with PdbTestInput([
82 >>> with PdbTestInput([
83 ... 'pdef example_function',
83 ... 'pdef example_function',
84 ... 'pdoc ExampleClass',
84 ... 'pdoc ExampleClass',
85 ... 'up',
85 ... 'up',
86 ... 'down',
86 ... 'down',
87 ... 'list',
87 ... 'list',
88 ... 'pinfo a',
88 ... 'pinfo a',
89 ... 'll',
89 ... 'll',
90 ... 'continue',
90 ... 'continue',
91 ... ]):
91 ... ]):
92 ... trigger_ipdb()
92 ... trigger_ipdb()
93 --Return--
93 --Return--
94 None
94 None
95 > <doctest ...>(3)trigger_ipdb()
95 > <doctest ...>(3)trigger_ipdb()
96 1 def trigger_ipdb():
96 1 def trigger_ipdb():
97 2 a = ExampleClass()
97 2 a = ExampleClass()
98 ----> 3 debugger.Pdb().set_trace()
98 ----> 3 debugger.Pdb().set_trace()
99 <BLANKLINE>
99 <BLANKLINE>
100 ipdb> pdef example_function
100 ipdb> pdef example_function
101 example_function(x, y, z='hello')
101 example_function(x, y, z='hello')
102 ipdb> pdoc ExampleClass
102 ipdb> pdoc ExampleClass
103 Class docstring:
103 Class docstring:
104 Docstring for ExampleClass.
104 Docstring for ExampleClass.
105 Init docstring:
105 Init docstring:
106 Docstring for ExampleClass.__init__
106 Docstring for ExampleClass.__init__
107 ipdb> up
107 ipdb> up
108 > <doctest ...>(11)<module>()
108 > <doctest ...>(11)<module>()
109 7 'pinfo a',
109 7 'pinfo a',
110 8 'll',
110 8 'll',
111 9 'continue',
111 9 'continue',
112 10 ]):
112 10 ]):
113 ---> 11 trigger_ipdb()
113 ---> 11 trigger_ipdb()
114 <BLANKLINE>
114 <BLANKLINE>
115 ipdb> down
115 ipdb> down
116 None
116 None
117 > <doctest ...>(3)trigger_ipdb()
117 > <doctest ...>(3)trigger_ipdb()
118 1 def trigger_ipdb():
118 1 def trigger_ipdb():
119 2 a = ExampleClass()
119 2 a = ExampleClass()
120 ----> 3 debugger.Pdb().set_trace()
120 ----> 3 debugger.Pdb().set_trace()
121 <BLANKLINE>
121 <BLANKLINE>
122 ipdb> list
122 ipdb> list
123 1 def trigger_ipdb():
123 1 def trigger_ipdb():
124 2 a = ExampleClass()
124 2 a = ExampleClass()
125 ----> 3 debugger.Pdb().set_trace()
125 ----> 3 debugger.Pdb().set_trace()
126 <BLANKLINE>
126 <BLANKLINE>
127 ipdb> pinfo a
127 ipdb> pinfo a
128 Type: ExampleClass
128 Type: ExampleClass
129 String form: ExampleClass()
129 String form: ExampleClass()
130 Namespace: Local...
130 Namespace: Local...
131 Docstring: Docstring for ExampleClass.
131 Docstring: Docstring for ExampleClass.
132 Init docstring: Docstring for ExampleClass.__init__
132 Init docstring: Docstring for ExampleClass.__init__
133 ipdb> ll
133 ipdb> ll
134 1 def trigger_ipdb():
134 1 def trigger_ipdb():
135 2 a = ExampleClass()
135 2 a = ExampleClass()
136 ----> 3 debugger.Pdb().set_trace()
136 ----> 3 debugger.Pdb().set_trace()
137 <BLANKLINE>
137 <BLANKLINE>
138 ipdb> continue
138 ipdb> continue
139
139
140 Restore previous trace function, e.g. for coverage.py
140 Restore previous trace function, e.g. for coverage.py
141
141
142 >>> sys.settrace(old_trace)
142 >>> sys.settrace(old_trace)
143 '''
143 '''
144
144
145 def test_ipdb_magics2():
145 def test_ipdb_magics2():
146 '''Test ipdb with a very short function.
146 '''Test ipdb with a very short function.
147
147
148 >>> old_trace = sys.gettrace()
148 >>> old_trace = sys.gettrace()
149
149
150 >>> def bar():
150 >>> def bar():
151 ... pass
151 ... pass
152
152
153 Run ipdb.
153 Run ipdb.
154
154
155 >>> with PdbTestInput([
155 >>> with PdbTestInput([
156 ... 'continue',
156 ... 'continue',
157 ... ]):
157 ... ]):
158 ... debugger.Pdb().runcall(bar)
158 ... debugger.Pdb().runcall(bar)
159 > <doctest ...>(2)bar()
159 > <doctest ...>(2)bar()
160 1 def bar():
160 1 def bar():
161 ----> 2 pass
161 ----> 2 pass
162 <BLANKLINE>
162 <BLANKLINE>
163 ipdb> continue
163 ipdb> continue
164
164
165 Restore previous trace function, e.g. for coverage.py
165 Restore previous trace function, e.g. for coverage.py
166
166
167 >>> sys.settrace(old_trace)
167 >>> sys.settrace(old_trace)
168 '''
168 '''
169
169
170 def can_quit():
170 def can_quit():
171 '''Test that quit work in ipydb
171 '''Test that quit work in ipydb
172
172
173 >>> old_trace = sys.gettrace()
173 >>> old_trace = sys.gettrace()
174
174
175 >>> def bar():
175 >>> def bar():
176 ... pass
176 ... pass
177
177
178 >>> with PdbTestInput([
178 >>> with PdbTestInput([
179 ... 'quit',
179 ... 'quit',
180 ... ]):
180 ... ]):
181 ... debugger.Pdb().runcall(bar)
181 ... debugger.Pdb().runcall(bar)
182 > <doctest ...>(2)bar()
182 > <doctest ...>(2)bar()
183 1 def bar():
183 1 def bar():
184 ----> 2 pass
184 ----> 2 pass
185 <BLANKLINE>
185 <BLANKLINE>
186 ipdb> quit
186 ipdb> quit
187
187
188 Restore previous trace function, e.g. for coverage.py
188 Restore previous trace function, e.g. for coverage.py
189
189
190 >>> sys.settrace(old_trace)
190 >>> sys.settrace(old_trace)
191 '''
191 '''
192
192
193
193
194 def can_exit():
194 def can_exit():
195 '''Test that quit work in ipydb
195 '''Test that quit work in ipydb
196
196
197 >>> old_trace = sys.gettrace()
197 >>> old_trace = sys.gettrace()
198
198
199 >>> def bar():
199 >>> def bar():
200 ... pass
200 ... pass
201
201
202 >>> with PdbTestInput([
202 >>> with PdbTestInput([
203 ... 'exit',
203 ... 'exit',
204 ... ]):
204 ... ]):
205 ... debugger.Pdb().runcall(bar)
205 ... debugger.Pdb().runcall(bar)
206 > <doctest ...>(2)bar()
206 > <doctest ...>(2)bar()
207 1 def bar():
207 1 def bar():
208 ----> 2 pass
208 ----> 2 pass
209 <BLANKLINE>
209 <BLANKLINE>
210 ipdb> exit
210 ipdb> exit
211
211
212 Restore previous trace function, e.g. for coverage.py
212 Restore previous trace function, e.g. for coverage.py
213
213
214 >>> sys.settrace(old_trace)
214 >>> sys.settrace(old_trace)
215 '''
215 '''
216
216
217
217
218 def test_interruptible_core_debugger():
218 def test_interruptible_core_debugger():
219 """The debugger can be interrupted.
219 """The debugger can be interrupted.
220
220
221 The presumption is there is some mechanism that causes a KeyboardInterrupt
221 The presumption is there is some mechanism that causes a KeyboardInterrupt
222 (this is implemented in ipykernel). We want to ensure the
222 (this is implemented in ipykernel). We want to ensure the
223 KeyboardInterrupt cause debugging to cease.
223 KeyboardInterrupt cause debugging to cease.
224 """
224 """
225 def raising_input(msg="", called=[0]):
225 def raising_input(msg="", called=[0]):
226 called[0] += 1
226 called[0] += 1
227 assert called[0] == 1, "input() should only be called once!"
227 assert called[0] == 1, "input() should only be called once!"
228 raise KeyboardInterrupt()
228 raise KeyboardInterrupt()
229
229
230 tracer_orig = sys.gettrace()
230 tracer_orig = sys.gettrace()
231 try:
231 try:
232 with patch.object(builtins, "input", raising_input):
232 with patch.object(builtins, "input", raising_input):
233 debugger.InterruptiblePdb().set_trace()
233 debugger.InterruptiblePdb().set_trace()
234 # The way this test will fail is by set_trace() never exiting,
234 # The way this test will fail is by set_trace() never exiting,
235 # resulting in a timeout by the test runner. The alternative
235 # resulting in a timeout by the test runner. The alternative
236 # implementation would involve a subprocess, but that adds issues
236 # implementation would involve a subprocess, but that adds issues
237 # with interrupting subprocesses that are rather complex, so it's
237 # with interrupting subprocesses that are rather complex, so it's
238 # simpler just to do it this way.
238 # simpler just to do it this way.
239 finally:
239 finally:
240 # restore the original trace function
240 # restore the original trace function
241 sys.settrace(tracer_orig)
241 sys.settrace(tracer_orig)
242
242
243
243
244 @skip_win32
244 @skip_win32
245 def test_xmode_skip():
245 def test_xmode_skip():
246 """that xmode skip frames
246 """that xmode skip frames
247
247
248 Not as a doctest as pytest does not run doctests.
248 Not as a doctest as pytest does not run doctests.
249 """
249 """
250 import pexpect
250 import pexpect
251 env = os.environ.copy()
251 env = os.environ.copy()
252 env["IPY_TEST_SIMPLE_PROMPT"] = "1"
252 env["IPY_TEST_SIMPLE_PROMPT"] = "1"
253
253
254 child = pexpect.spawn(
254 child = pexpect.spawn(
255 sys.executable, ["-m", "IPython", "--colors=nocolor"], env=env
255 sys.executable, ["-m", "IPython", "--colors=nocolor"], env=env
256 )
256 )
257 child.timeout = 15 * IPYTHON_TESTING_TIMEOUT_SCALE
257 child.timeout = 15 * IPYTHON_TESTING_TIMEOUT_SCALE
258
258
259 child.expect("IPython")
259 child.expect("IPython")
260 child.expect("\n")
260 child.expect("\n")
261 child.expect_exact("In [1]")
261 child.expect_exact("In [1]")
262
262
263 block = dedent(
263 block = dedent(
264 """
264 """
265 def f():
265 def f():
266 __tracebackhide__ = True
266 __tracebackhide__ = True
267 g()
267 g()
268
268
269 def g():
269 def g():
270 raise ValueError
270 raise ValueError
271
271
272 f()
272 f()
273 """
273 """
274 )
274 )
275
275
276 for line in block.splitlines():
276 for line in block.splitlines():
277 child.sendline(line)
277 child.sendline(line)
278 child.expect_exact(line)
278 child.expect_exact(line)
279 child.expect_exact("skipping")
279 child.expect_exact("skipping")
280
280
281 block = dedent(
281 block = dedent(
282 """
282 """
283 def f():
283 def f():
284 __tracebackhide__ = True
284 __tracebackhide__ = True
285 g()
285 g()
286
286
287 def g():
287 def g():
288 from IPython.core.debugger import set_trace
288 from IPython.core.debugger import set_trace
289 set_trace()
289 set_trace()
290
290
291 f()
291 f()
292 """
292 """
293 )
293 )
294
294
295 for line in block.splitlines():
295 for line in block.splitlines():
296 child.sendline(line)
296 child.sendline(line)
297 child.expect_exact(line)
297 child.expect_exact(line)
298
298
299 child.expect("ipdb>")
299 child.expect("ipdb>")
300 child.sendline("w")
300 child.sendline("w")
301 child.expect("hidden")
301 child.expect("hidden")
302 child.expect("ipdb>")
302 child.expect("ipdb>")
303 child.sendline("skip_hidden false")
303 child.sendline("skip_hidden false")
304 child.sendline("w")
304 child.sendline("w")
305 child.expect("__traceba")
305 child.expect("__traceba")
306 child.expect("ipdb>")
306 child.expect("ipdb>")
307
307
308 child.close()
308 child.close()
309
309
310
310
311 skip_decorators_blocks = (
311 skip_decorators_blocks = (
312 """
312 """
313 def helpers_helper():
313 def helpers_helper():
314 pass # should not stop here except breakpoint
314 pass # should not stop here except breakpoint
315 """,
315 """,
316 """
316 """
317 def helper_1():
317 def helper_1():
318 helpers_helper() # should not stop here
318 helpers_helper() # should not stop here
319 """,
319 """,
320 """
320 """
321 def helper_2():
321 def helper_2():
322 pass # should not stop here
322 pass # should not stop here
323 """,
323 """,
324 """
324 """
325 def pdb_skipped_decorator2(function):
325 def pdb_skipped_decorator2(function):
326 def wrapped_fn(*args, **kwargs):
326 def wrapped_fn(*args, **kwargs):
327 __debuggerskip__ = True
327 __debuggerskip__ = True
328 helper_2()
328 helper_2()
329 __debuggerskip__ = False
329 __debuggerskip__ = False
330 result = function(*args, **kwargs)
330 result = function(*args, **kwargs)
331 __debuggerskip__ = True
331 __debuggerskip__ = True
332 helper_2()
332 helper_2()
333 return result
333 return result
334 return wrapped_fn
334 return wrapped_fn
335 """,
335 """,
336 """
336 """
337 def pdb_skipped_decorator(function):
337 def pdb_skipped_decorator(function):
338 def wrapped_fn(*args, **kwargs):
338 def wrapped_fn(*args, **kwargs):
339 __debuggerskip__ = True
339 __debuggerskip__ = True
340 helper_1()
340 helper_1()
341 __debuggerskip__ = False
341 __debuggerskip__ = False
342 result = function(*args, **kwargs)
342 result = function(*args, **kwargs)
343 __debuggerskip__ = True
343 __debuggerskip__ = True
344 helper_2()
344 helper_2()
345 return result
345 return result
346 return wrapped_fn
346 return wrapped_fn
347 """,
347 """,
348 """
348 """
349 @pdb_skipped_decorator
349 @pdb_skipped_decorator
350 @pdb_skipped_decorator2
350 @pdb_skipped_decorator2
351 def bar(x, y):
351 def bar(x, y):
352 return x * y
352 return x * y
353 """,
353 """,
354 """import IPython.terminal.debugger as ipdb""",
354 """import IPython.terminal.debugger as ipdb""",
355 """
355 """
356 def f():
356 def f():
357 ipdb.set_trace()
357 ipdb.set_trace()
358 bar(3, 4)
358 bar(3, 4)
359 """,
359 """,
360 """
360 """
361 f()
361 f()
362 """,
362 """,
363 )
363 )
364
364
365
365
366 def _decorator_skip_setup():
366 def _decorator_skip_setup():
367 import pexpect
367 import pexpect
368
368
369 env = os.environ.copy()
369 env = os.environ.copy()
370 env["IPY_TEST_SIMPLE_PROMPT"] = "1"
370 env["IPY_TEST_SIMPLE_PROMPT"] = "1"
371 env["PROMPT_TOOLKIT_NO_CPR"] = "1"
371 env["PROMPT_TOOLKIT_NO_CPR"] = "1"
372
372
373 child = pexpect.spawn(
373 child = pexpect.spawn(
374 sys.executable, ["-m", "IPython", "--colors=nocolor"], env=env
374 sys.executable, ["-m", "IPython", "--colors=nocolor"], env=env
375 )
375 )
376 child.timeout = 15 * IPYTHON_TESTING_TIMEOUT_SCALE
376 child.timeout = 15 * IPYTHON_TESTING_TIMEOUT_SCALE
377
377
378 child.expect("IPython")
378 child.expect("IPython")
379 child.expect("\n")
379 child.expect("\n")
380
380
381 child.timeout = 5 * IPYTHON_TESTING_TIMEOUT_SCALE
381 child.timeout = 5 * IPYTHON_TESTING_TIMEOUT_SCALE
382 child.str_last_chars = 500
382 child.str_last_chars = 500
383
383
384 dedented_blocks = [dedent(b).strip() for b in skip_decorators_blocks]
384 dedented_blocks = [dedent(b).strip() for b in skip_decorators_blocks]
385 in_prompt_number = 1
385 in_prompt_number = 1
386 for cblock in dedented_blocks:
386 for cblock in dedented_blocks:
387 child.expect_exact(f"In [{in_prompt_number}]:")
387 child.expect_exact(f"In [{in_prompt_number}]:")
388 in_prompt_number += 1
388 in_prompt_number += 1
389 for line in cblock.splitlines():
389 for line in cblock.splitlines():
390 child.sendline(line)
390 child.sendline(line)
391 child.expect_exact(line)
391 child.expect_exact(line)
392 child.sendline("")
392 child.sendline("")
393 return child
393 return child
394
394
395
395
396 @pytest.mark.skip(reason="recently fail for unknown reason on CI")
396 @pytest.mark.skip(reason="recently fail for unknown reason on CI")
397 @skip_win32
397 @skip_win32
398 def test_decorator_skip():
398 def test_decorator_skip():
399 """test that decorator frames can be skipped."""
399 """test that decorator frames can be skipped."""
400
400
401 child = _decorator_skip_setup()
401 child = _decorator_skip_setup()
402
402
403 child.expect_exact("ipython-input-8")
403 child.expect_exact("ipython-input-8")
404 child.expect_exact("3 bar(3, 4)")
404 child.expect_exact("3 bar(3, 4)")
405 child.expect("ipdb>")
405 child.expect("ipdb>")
406
406
407 child.expect("ipdb>")
407 child.expect("ipdb>")
408 child.sendline("step")
408 child.sendline("step")
409 child.expect_exact("step")
409 child.expect_exact("step")
410 child.expect_exact("--Call--")
410 child.expect_exact("--Call--")
411 child.expect_exact("ipython-input-6")
411 child.expect_exact("ipython-input-6")
412
412
413 child.expect_exact("1 @pdb_skipped_decorator")
413 child.expect_exact("1 @pdb_skipped_decorator")
414
414
415 child.sendline("s")
415 child.sendline("s")
416 child.expect_exact("return x * y")
416 child.expect_exact("return x * y")
417
417
418 child.close()
418 child.close()
419
419
420
420
421 @pytest.mark.skip(reason="recently fail for unknown reason on CI")
421 @pytest.mark.skip(reason="recently fail for unknown reason on CI")
422 @pytest.mark.skipif(platform.python_implementation() == "PyPy", reason="issues on PyPy")
422 @pytest.mark.skipif(platform.python_implementation() == "PyPy", reason="issues on PyPy")
423 @skip_win32
423 @skip_win32
424 def test_decorator_skip_disabled():
424 def test_decorator_skip_disabled():
425 """test that decorator frame skipping can be disabled"""
425 """test that decorator frame skipping can be disabled"""
426
426
427 child = _decorator_skip_setup()
427 child = _decorator_skip_setup()
428
428
429 child.expect_exact("3 bar(3, 4)")
429 child.expect_exact("3 bar(3, 4)")
430
430
431 for input_, expected in [
431 for input_, expected in [
432 ("skip_predicates debuggerskip False", ""),
432 ("skip_predicates debuggerskip False", ""),
433 ("skip_predicates", "debuggerskip : False"),
433 ("skip_predicates", "debuggerskip : False"),
434 ("step", "---> 2 def wrapped_fn"),
434 ("step", "---> 2 def wrapped_fn"),
435 ("step", "----> 3 __debuggerskip__"),
435 ("step", "----> 3 __debuggerskip__"),
436 ("step", "----> 4 helper_1()"),
436 ("step", "----> 4 helper_1()"),
437 ("step", "---> 1 def helper_1():"),
437 ("step", "---> 1 def helper_1():"),
438 ("next", "----> 2 helpers_helper()"),
438 ("next", "----> 2 helpers_helper()"),
439 ("next", "--Return--"),
439 ("next", "--Return--"),
440 ("next", "----> 5 __debuggerskip__ = False"),
440 ("next", "----> 5 __debuggerskip__ = False"),
441 ]:
441 ]:
442 child.expect("ipdb>")
442 child.expect("ipdb>")
443 child.sendline(input_)
443 child.sendline(input_)
444 child.expect_exact(input_)
444 child.expect_exact(input_)
445 child.expect_exact(expected)
445 child.expect_exact(expected)
446
446
447 child.close()
447 child.close()
448
448
449
449
450 @pytest.mark.xfail(
451 sys.version_info.releaselevel not in ("final", "candidate"),
452 reason="fails on 3.13.dev",
453 strict=True,
454 )
450 @pytest.mark.skipif(platform.python_implementation() == "PyPy", reason="issues on PyPy")
455 @pytest.mark.skipif(platform.python_implementation() == "PyPy", reason="issues on PyPy")
451 @skip_win32
456 @skip_win32
452 def test_decorator_skip_with_breakpoint():
457 def test_decorator_skip_with_breakpoint():
453 """test that decorator frame skipping can be disabled"""
458 """test that decorator frame skipping can be disabled"""
454
459
455 import pexpect
460 import pexpect
456
461
457 env = os.environ.copy()
462 env = os.environ.copy()
458 env["IPY_TEST_SIMPLE_PROMPT"] = "1"
463 env["IPY_TEST_SIMPLE_PROMPT"] = "1"
459 env["PROMPT_TOOLKIT_NO_CPR"] = "1"
464 env["PROMPT_TOOLKIT_NO_CPR"] = "1"
460
465
461 child = pexpect.spawn(
466 child = pexpect.spawn(
462 sys.executable, ["-m", "IPython", "--colors=nocolor"], env=env
467 sys.executable, ["-m", "IPython", "--colors=nocolor"], env=env
463 )
468 )
464 child.timeout = 15 * IPYTHON_TESTING_TIMEOUT_SCALE
469 child.timeout = 15 * IPYTHON_TESTING_TIMEOUT_SCALE
465 child.str_last_chars = 500
470 child.str_last_chars = 500
466
471
467 child.expect("IPython")
472 child.expect("IPython")
468 child.expect("\n")
473 child.expect("\n")
469
474
470 child.timeout = 5 * IPYTHON_TESTING_TIMEOUT_SCALE
475 child.timeout = 5 * IPYTHON_TESTING_TIMEOUT_SCALE
471
476
472 ### we need a filename, so we need to exec the full block with a filename
477 ### we need a filename, so we need to exec the full block with a filename
473 with NamedTemporaryFile(suffix=".py", dir=".", delete=True) as tf:
478 with NamedTemporaryFile(suffix=".py", dir=".", delete=True) as tf:
474 name = tf.name[:-3].split("/")[-1]
479 name = tf.name[:-3].split("/")[-1]
475 tf.write("\n".join([dedent(x) for x in skip_decorators_blocks[:-1]]).encode())
480 tf.write("\n".join([dedent(x) for x in skip_decorators_blocks[:-1]]).encode())
476 tf.flush()
481 tf.flush()
477 codeblock = f"from {name} import f"
482 codeblock = f"from {name} import f"
478
483
479 dedented_blocks = [
484 dedented_blocks = [
480 codeblock,
485 codeblock,
481 "f()",
486 "f()",
482 ]
487 ]
483
488
484 in_prompt_number = 1
489 in_prompt_number = 1
485 for cblock in dedented_blocks:
490 for cblock in dedented_blocks:
486 child.expect_exact(f"In [{in_prompt_number}]:")
491 child.expect_exact(f"In [{in_prompt_number}]:")
487 in_prompt_number += 1
492 in_prompt_number += 1
488 for line in cblock.splitlines():
493 for line in cblock.splitlines():
489 child.sendline(line)
494 child.sendline(line)
490 child.expect_exact(line)
495 child.expect_exact(line)
491 child.sendline("")
496 child.sendline("")
492
497
493 # as the filename does not exists, we'll rely on the filename prompt
498 # as the filename does not exists, we'll rely on the filename prompt
494 child.expect_exact("47 bar(3, 4)")
499 child.expect_exact("47 bar(3, 4)")
495
500
496 for input_, expected in [
501 for input_, expected in [
497 (f"b {name}.py:3", ""),
502 (f"b {name}.py:3", ""),
498 ("step", "1---> 3 pass # should not stop here except"),
503 ("step", "1---> 3 pass # should not stop here except"),
499 ("step", "---> 38 @pdb_skipped_decorator"),
504 ("step", "---> 38 @pdb_skipped_decorator"),
500 ("continue", ""),
505 ("continue", ""),
501 ]:
506 ]:
502 child.expect("ipdb>")
507 child.expect("ipdb>")
503 child.sendline(input_)
508 child.sendline(input_)
504 child.expect_exact(input_)
509 child.expect_exact(input_)
505 child.expect_exact(expected)
510 child.expect_exact(expected)
506
511
507 child.close()
512 child.close()
508
513
509
514
510 @skip_win32
515 @skip_win32
511 def test_where_erase_value():
516 def test_where_erase_value():
512 """Test that `where` does not access f_locals and erase values."""
517 """Test that `where` does not access f_locals and erase values."""
513 import pexpect
518 import pexpect
514
519
515 env = os.environ.copy()
520 env = os.environ.copy()
516 env["IPY_TEST_SIMPLE_PROMPT"] = "1"
521 env["IPY_TEST_SIMPLE_PROMPT"] = "1"
517
522
518 child = pexpect.spawn(
523 child = pexpect.spawn(
519 sys.executable, ["-m", "IPython", "--colors=nocolor"], env=env
524 sys.executable, ["-m", "IPython", "--colors=nocolor"], env=env
520 )
525 )
521 child.timeout = 15 * IPYTHON_TESTING_TIMEOUT_SCALE
526 child.timeout = 15 * IPYTHON_TESTING_TIMEOUT_SCALE
522
527
523 child.expect("IPython")
528 child.expect("IPython")
524 child.expect("\n")
529 child.expect("\n")
525 child.expect_exact("In [1]")
530 child.expect_exact("In [1]")
526
531
527 block = dedent(
532 block = dedent(
528 """
533 """
529 def simple_f():
534 def simple_f():
530 myvar = 1
535 myvar = 1
531 print(myvar)
536 print(myvar)
532 1/0
537 1/0
533 print(myvar)
538 print(myvar)
534 simple_f() """
539 simple_f() """
535 )
540 )
536
541
537 for line in block.splitlines():
542 for line in block.splitlines():
538 child.sendline(line)
543 child.sendline(line)
539 child.expect_exact(line)
544 child.expect_exact(line)
540 child.expect_exact("ZeroDivisionError")
545 child.expect_exact("ZeroDivisionError")
541 child.expect_exact("In [2]:")
546 child.expect_exact("In [2]:")
542
547
543 child.sendline("%debug")
548 child.sendline("%debug")
544
549
545 ##
550 ##
546 child.expect("ipdb>")
551 child.expect("ipdb>")
547
552
548 child.sendline("myvar")
553 child.sendline("myvar")
549 child.expect("1")
554 child.expect("1")
550
555
551 ##
556 ##
552 child.expect("ipdb>")
557 child.expect("ipdb>")
553
558
554 child.sendline("myvar = 2")
559 child.sendline("myvar = 2")
555
560
556 ##
561 ##
557 child.expect_exact("ipdb>")
562 child.expect_exact("ipdb>")
558
563
559 child.sendline("myvar")
564 child.sendline("myvar")
560
565
561 child.expect_exact("2")
566 child.expect_exact("2")
562
567
563 ##
568 ##
564 child.expect("ipdb>")
569 child.expect("ipdb>")
565 child.sendline("where")
570 child.sendline("where")
566
571
567 ##
572 ##
568 child.expect("ipdb>")
573 child.expect("ipdb>")
569 child.sendline("myvar")
574 child.sendline("myvar")
570
575
571 child.expect_exact("2")
576 child.expect_exact("2")
572 child.expect("ipdb>")
577 child.expect("ipdb>")
573
578
574 child.close()
579 child.close()
@@ -1,579 +1,589
1 """Tests for the object inspection functionality.
1 """Tests for the object inspection functionality.
2 """
2 """
3
3
4 # Copyright (c) IPython Development Team.
4 # Copyright (c) IPython Development Team.
5 # Distributed under the terms of the Modified BSD License.
5 # Distributed under the terms of the Modified BSD License.
6
6
7
7
8 from contextlib import contextmanager
8 from contextlib import contextmanager
9 from inspect import signature, Signature, Parameter
9 from inspect import signature, Signature, Parameter
10 import inspect
10 import inspect
11 import os
11 import os
12 import pytest
12 import pytest
13 import re
13 import re
14 import sys
14 import sys
15
15
16 from .. import oinspect
16 from .. import oinspect
17
17
18 from decorator import decorator
18 from decorator import decorator
19
19
20 from IPython.testing.tools import AssertPrints, AssertNotPrints
20 from IPython.testing.tools import AssertPrints, AssertNotPrints
21 from IPython.utils.path import compress_user
21 from IPython.utils.path import compress_user
22
22
23
23
24 #-----------------------------------------------------------------------------
24 #-----------------------------------------------------------------------------
25 # Globals and constants
25 # Globals and constants
26 #-----------------------------------------------------------------------------
26 #-----------------------------------------------------------------------------
27
27
28 inspector = None
28 inspector = None
29
29
30 def setup_module():
30 def setup_module():
31 global inspector
31 global inspector
32 inspector = oinspect.Inspector()
32 inspector = oinspect.Inspector()
33
33
34
34
35 class SourceModuleMainTest:
35 class SourceModuleMainTest:
36 __module__ = "__main__"
36 __module__ = "__main__"
37
37
38
38
39 #-----------------------------------------------------------------------------
39 #-----------------------------------------------------------------------------
40 # Local utilities
40 # Local utilities
41 #-----------------------------------------------------------------------------
41 #-----------------------------------------------------------------------------
42
42
43 # WARNING: since this test checks the line number where a function is
43 # WARNING: since this test checks the line number where a function is
44 # defined, if any code is inserted above, the following line will need to be
44 # defined, if any code is inserted above, the following line will need to be
45 # updated. Do NOT insert any whitespace between the next line and the function
45 # updated. Do NOT insert any whitespace between the next line and the function
46 # definition below.
46 # definition below.
47 THIS_LINE_NUMBER = 47 # Put here the actual number of this line
47 THIS_LINE_NUMBER = 47 # Put here the actual number of this line
48
48
49
49
50 def test_find_source_lines():
50 def test_find_source_lines():
51 assert oinspect.find_source_lines(test_find_source_lines) == THIS_LINE_NUMBER + 3
51 assert oinspect.find_source_lines(test_find_source_lines) == THIS_LINE_NUMBER + 3
52 assert oinspect.find_source_lines(type) is None
52 assert oinspect.find_source_lines(type) is None
53 assert oinspect.find_source_lines(SourceModuleMainTest) is None
53 assert oinspect.find_source_lines(SourceModuleMainTest) is None
54 assert oinspect.find_source_lines(SourceModuleMainTest()) is None
54 assert oinspect.find_source_lines(SourceModuleMainTest()) is None
55
55
56
56
57 def test_getsource():
57 def test_getsource():
58 assert oinspect.getsource(type) is None
58 assert oinspect.getsource(type) is None
59 assert oinspect.getsource(SourceModuleMainTest) is None
59 assert oinspect.getsource(SourceModuleMainTest) is None
60 assert oinspect.getsource(SourceModuleMainTest()) is None
60 assert oinspect.getsource(SourceModuleMainTest()) is None
61
61
62
62
63 def test_inspect_getfile_raises_exception():
63 def test_inspect_getfile_raises_exception():
64 """Check oinspect.find_file/getsource/find_source_lines expectations"""
64 """Check oinspect.find_file/getsource/find_source_lines expectations"""
65 with pytest.raises(TypeError):
65 with pytest.raises(TypeError):
66 inspect.getfile(type)
66 inspect.getfile(type)
67 with pytest.raises(OSError):
67 with pytest.raises(OSError):
68 inspect.getfile(SourceModuleMainTest)
68 inspect.getfile(SourceModuleMainTest)
69
69
70
70
71 # A couple of utilities to ensure these tests work the same from a source or a
71 # A couple of utilities to ensure these tests work the same from a source or a
72 # binary install
72 # binary install
73 def pyfile(fname):
73 def pyfile(fname):
74 return os.path.normcase(re.sub('.py[co]$', '.py', fname))
74 return os.path.normcase(re.sub('.py[co]$', '.py', fname))
75
75
76
76
77 def match_pyfiles(f1, f2):
77 def match_pyfiles(f1, f2):
78 assert pyfile(f1) == pyfile(f2)
78 assert pyfile(f1) == pyfile(f2)
79
79
80
80
81 def test_find_file():
81 def test_find_file():
82 match_pyfiles(oinspect.find_file(test_find_file), os.path.abspath(__file__))
82 match_pyfiles(oinspect.find_file(test_find_file), os.path.abspath(__file__))
83 assert oinspect.find_file(type) is None
83 assert oinspect.find_file(type) is None
84 assert oinspect.find_file(SourceModuleMainTest) is None
84 assert oinspect.find_file(SourceModuleMainTest) is None
85 assert oinspect.find_file(SourceModuleMainTest()) is None
85 assert oinspect.find_file(SourceModuleMainTest()) is None
86
86
87
87
88 def test_find_file_decorated1():
88 def test_find_file_decorated1():
89
89
90 @decorator
90 @decorator
91 def noop1(f):
91 def noop1(f):
92 def wrapper(*a, **kw):
92 def wrapper(*a, **kw):
93 return f(*a, **kw)
93 return f(*a, **kw)
94 return wrapper
94 return wrapper
95
95
96 @noop1
96 @noop1
97 def f(x):
97 def f(x):
98 "My docstring"
98 "My docstring"
99
99
100 match_pyfiles(oinspect.find_file(f), os.path.abspath(__file__))
100 match_pyfiles(oinspect.find_file(f), os.path.abspath(__file__))
101 assert f.__doc__ == "My docstring"
101 assert f.__doc__ == "My docstring"
102
102
103
103
104 def test_find_file_decorated2():
104 def test_find_file_decorated2():
105
105
106 @decorator
106 @decorator
107 def noop2(f, *a, **kw):
107 def noop2(f, *a, **kw):
108 return f(*a, **kw)
108 return f(*a, **kw)
109
109
110 @noop2
110 @noop2
111 @noop2
111 @noop2
112 @noop2
112 @noop2
113 def f(x):
113 def f(x):
114 "My docstring 2"
114 "My docstring 2"
115
115
116 match_pyfiles(oinspect.find_file(f), os.path.abspath(__file__))
116 match_pyfiles(oinspect.find_file(f), os.path.abspath(__file__))
117 assert f.__doc__ == "My docstring 2"
117 assert f.__doc__ == "My docstring 2"
118
118
119
119
120 def test_find_file_magic():
120 def test_find_file_magic():
121 run = ip.find_line_magic('run')
121 run = ip.find_line_magic('run')
122 assert oinspect.find_file(run) is not None
122 assert oinspect.find_file(run) is not None
123
123
124
124
125 # A few generic objects we can then inspect in the tests below
125 # A few generic objects we can then inspect in the tests below
126
126
127 class Call(object):
127 class Call(object):
128 """This is the class docstring."""
128 """This is the class docstring."""
129
129
130 def __init__(self, x, y=1):
130 def __init__(self, x, y=1):
131 """This is the constructor docstring."""
131 """This is the constructor docstring."""
132
132
133 def __call__(self, *a, **kw):
133 def __call__(self, *a, **kw):
134 """This is the call docstring."""
134 """This is the call docstring."""
135
135
136 def method(self, x, z=2):
136 def method(self, x, z=2):
137 """Some method's docstring"""
137 """Some method's docstring"""
138
138
139 class HasSignature(object):
139 class HasSignature(object):
140 """This is the class docstring."""
140 """This is the class docstring."""
141 __signature__ = Signature([Parameter('test', Parameter.POSITIONAL_OR_KEYWORD)])
141 __signature__ = Signature([Parameter('test', Parameter.POSITIONAL_OR_KEYWORD)])
142
142
143 def __init__(self, *args):
143 def __init__(self, *args):
144 """This is the init docstring"""
144 """This is the init docstring"""
145
145
146
146
147 class SimpleClass(object):
147 class SimpleClass(object):
148 def method(self, x, z=2):
148 def method(self, x, z=2):
149 """Some method's docstring"""
149 """Some method's docstring"""
150
150
151
151
152 class Awkward(object):
152 class Awkward(object):
153 def __getattr__(self, name):
153 def __getattr__(self, name):
154 raise Exception(name)
154 raise Exception(name)
155
155
156 class NoBoolCall:
156 class NoBoolCall:
157 """
157 """
158 callable with `__bool__` raising should still be inspect-able.
158 callable with `__bool__` raising should still be inspect-able.
159 """
159 """
160
160
161 def __call__(self):
161 def __call__(self):
162 """does nothing"""
162 """does nothing"""
163 pass
163 pass
164
164
165 def __bool__(self):
165 def __bool__(self):
166 """just raise NotImplemented"""
166 """just raise NotImplemented"""
167 raise NotImplementedError('Must be implemented')
167 raise NotImplementedError('Must be implemented')
168
168
169
169
170 class SerialLiar(object):
170 class SerialLiar(object):
171 """Attribute accesses always get another copy of the same class.
171 """Attribute accesses always get another copy of the same class.
172
172
173 unittest.mock.call does something similar, but it's not ideal for testing
173 unittest.mock.call does something similar, but it's not ideal for testing
174 as the failure mode is to eat all your RAM. This gives up after 10k levels.
174 as the failure mode is to eat all your RAM. This gives up after 10k levels.
175 """
175 """
176 def __init__(self, max_fibbing_twig, lies_told=0):
176 def __init__(self, max_fibbing_twig, lies_told=0):
177 if lies_told > 10000:
177 if lies_told > 10000:
178 raise RuntimeError('Nose too long, honesty is the best policy')
178 raise RuntimeError('Nose too long, honesty is the best policy')
179 self.max_fibbing_twig = max_fibbing_twig
179 self.max_fibbing_twig = max_fibbing_twig
180 self.lies_told = lies_told
180 self.lies_told = lies_told
181 max_fibbing_twig[0] = max(max_fibbing_twig[0], lies_told)
181 max_fibbing_twig[0] = max(max_fibbing_twig[0], lies_told)
182
182
183 def __getattr__(self, item):
183 def __getattr__(self, item):
184 return SerialLiar(self.max_fibbing_twig, self.lies_told + 1)
184 return SerialLiar(self.max_fibbing_twig, self.lies_told + 1)
185
185
186 #-----------------------------------------------------------------------------
186 #-----------------------------------------------------------------------------
187 # Tests
187 # Tests
188 #-----------------------------------------------------------------------------
188 #-----------------------------------------------------------------------------
189
189
190 def test_info():
190 def test_info():
191 "Check that Inspector.info fills out various fields as expected."
191 "Check that Inspector.info fills out various fields as expected."
192 i = inspector.info(Call, oname="Call")
192 i = inspector.info(Call, oname="Call")
193 assert i["type_name"] == "type"
193 assert i["type_name"] == "type"
194 expected_class = str(type(type)) # <class 'type'> (Python 3) or <type 'type'>
194 expected_class = str(type(type)) # <class 'type'> (Python 3) or <type 'type'>
195 assert i["base_class"] == expected_class
195 assert i["base_class"] == expected_class
196 assert re.search(
196 assert re.search(
197 "<class 'IPython.core.tests.test_oinspect.Call'( at 0x[0-9a-f]{1,9})?>",
197 "<class 'IPython.core.tests.test_oinspect.Call'( at 0x[0-9a-f]{1,9})?>",
198 i["string_form"],
198 i["string_form"],
199 )
199 )
200 fname = __file__
200 fname = __file__
201 if fname.endswith(".pyc"):
201 if fname.endswith(".pyc"):
202 fname = fname[:-1]
202 fname = fname[:-1]
203 # case-insensitive comparison needed on some filesystems
203 # case-insensitive comparison needed on some filesystems
204 # e.g. Windows:
204 # e.g. Windows:
205 assert i["file"].lower() == compress_user(fname).lower()
205 assert i["file"].lower() == compress_user(fname).lower()
206 assert i["definition"] == None
206 assert i["definition"] == None
207 assert i["docstring"] == Call.__doc__
207 assert i["docstring"] == Call.__doc__
208 assert i["source"] == None
208 assert i["source"] == None
209 assert i["isclass"] is True
209 assert i["isclass"] is True
210 assert i["init_definition"] == "Call(x, y=1)"
210 assert i["init_definition"] == "Call(x, y=1)"
211 assert i["init_docstring"] == Call.__init__.__doc__
211 assert i["init_docstring"] == Call.__init__.__doc__
212
212
213 i = inspector.info(Call, detail_level=1)
213 i = inspector.info(Call, detail_level=1)
214 assert i["source"] is not None
214 assert i["source"] is not None
215 assert i["docstring"] == None
215 assert i["docstring"] == None
216
216
217 c = Call(1)
217 c = Call(1)
218 c.__doc__ = "Modified instance docstring"
218 c.__doc__ = "Modified instance docstring"
219 i = inspector.info(c)
219 i = inspector.info(c)
220 assert i["type_name"] == "Call"
220 assert i["type_name"] == "Call"
221 assert i["docstring"] == "Modified instance docstring"
221 assert i["docstring"] == "Modified instance docstring"
222 assert i["class_docstring"] == Call.__doc__
222 assert i["class_docstring"] == Call.__doc__
223 assert i["init_docstring"] == Call.__init__.__doc__
223 assert i["init_docstring"] == Call.__init__.__doc__
224 assert i["call_docstring"] == Call.__call__.__doc__
224 assert i["call_docstring"] == Call.__call__.__doc__
225
225
226
226
227 def test_class_signature():
227 def test_class_signature():
228 info = inspector.info(HasSignature, "HasSignature")
228 info = inspector.info(HasSignature, "HasSignature")
229 assert info["init_definition"] == "HasSignature(test)"
229 assert info["init_definition"] == "HasSignature(test)"
230 assert info["init_docstring"] == HasSignature.__init__.__doc__
230 assert info["init_docstring"] == HasSignature.__init__.__doc__
231
231
232
232
233 def test_info_awkward():
233 def test_info_awkward():
234 # Just test that this doesn't throw an error.
234 # Just test that this doesn't throw an error.
235 inspector.info(Awkward())
235 inspector.info(Awkward())
236
236
237 def test_bool_raise():
237 def test_bool_raise():
238 inspector.info(NoBoolCall())
238 inspector.info(NoBoolCall())
239
239
240 def test_info_serialliar():
240 def test_info_serialliar():
241 fib_tracker = [0]
241 fib_tracker = [0]
242 inspector.info(SerialLiar(fib_tracker))
242 inspector.info(SerialLiar(fib_tracker))
243
243
244 # Nested attribute access should be cut off at 100 levels deep to avoid
244 # Nested attribute access should be cut off at 100 levels deep to avoid
245 # infinite loops: https://github.com/ipython/ipython/issues/9122
245 # infinite loops: https://github.com/ipython/ipython/issues/9122
246 assert fib_tracker[0] < 9000
246 assert fib_tracker[0] < 9000
247
247
248 def support_function_one(x, y=2, *a, **kw):
248 def support_function_one(x, y=2, *a, **kw):
249 """A simple function."""
249 """A simple function."""
250
250
251 def test_calldef_none():
251 def test_calldef_none():
252 # We should ignore __call__ for all of these.
252 # We should ignore __call__ for all of these.
253 for obj in [support_function_one, SimpleClass().method, any, str.upper]:
253 for obj in [support_function_one, SimpleClass().method, any, str.upper]:
254 i = inspector.info(obj)
254 i = inspector.info(obj)
255 assert i["call_def"] is None
255 assert i["call_def"] is None
256
256
257
257
258 def f_kwarg(pos, *, kwonly):
258 def f_kwarg(pos, *, kwonly):
259 pass
259 pass
260
260
261 def test_definition_kwonlyargs():
261 def test_definition_kwonlyargs():
262 i = inspector.info(f_kwarg, oname="f_kwarg") # analysis:ignore
262 i = inspector.info(f_kwarg, oname="f_kwarg") # analysis:ignore
263 assert i["definition"] == "f_kwarg(pos, *, kwonly)"
263 assert i["definition"] == "f_kwarg(pos, *, kwonly)"
264
264
265
265
266 def test_getdoc():
266 def test_getdoc():
267 class A(object):
267 class A(object):
268 """standard docstring"""
268 """standard docstring"""
269 pass
269 pass
270
270
271 class B(object):
271 class B(object):
272 """standard docstring"""
272 """standard docstring"""
273 def getdoc(self):
273 def getdoc(self):
274 return "custom docstring"
274 return "custom docstring"
275
275
276 class C(object):
276 class C(object):
277 """standard docstring"""
277 """standard docstring"""
278 def getdoc(self):
278 def getdoc(self):
279 return None
279 return None
280
280
281 a = A()
281 a = A()
282 b = B()
282 b = B()
283 c = C()
283 c = C()
284
284
285 assert oinspect.getdoc(a) == "standard docstring"
285 assert oinspect.getdoc(a) == "standard docstring"
286 assert oinspect.getdoc(b) == "custom docstring"
286 assert oinspect.getdoc(b) == "custom docstring"
287 assert oinspect.getdoc(c) == "standard docstring"
287 assert oinspect.getdoc(c) == "standard docstring"
288
288
289
289
290 def test_empty_property_has_no_source():
290 def test_empty_property_has_no_source():
291 i = inspector.info(property(), detail_level=1)
291 i = inspector.info(property(), detail_level=1)
292 assert i["source"] is None
292 assert i["source"] is None
293
293
294
294
295 def test_property_sources():
295 def test_property_sources():
296 # A simple adder whose source and signature stays
296 # A simple adder whose source and signature stays
297 # the same across Python distributions
297 # the same across Python distributions
298 def simple_add(a, b):
298 def simple_add(a, b):
299 "Adds two numbers"
299 "Adds two numbers"
300 return a + b
300 return a + b
301
301
302 class A(object):
302 class A(object):
303 @property
303 @property
304 def foo(self):
304 def foo(self):
305 return 'bar'
305 return 'bar'
306
306
307 foo = foo.setter(lambda self, v: setattr(self, 'bar', v))
307 foo = foo.setter(lambda self, v: setattr(self, 'bar', v))
308
308
309 dname = property(oinspect.getdoc)
309 dname = property(oinspect.getdoc)
310 adder = property(simple_add)
310 adder = property(simple_add)
311
311
312 i = inspector.info(A.foo, detail_level=1)
312 i = inspector.info(A.foo, detail_level=1)
313 assert "def foo(self):" in i["source"]
313 assert "def foo(self):" in i["source"]
314 assert "lambda self, v:" in i["source"]
314 assert "lambda self, v:" in i["source"]
315
315
316 i = inspector.info(A.dname, detail_level=1)
316 i = inspector.info(A.dname, detail_level=1)
317 assert "def getdoc(obj)" in i["source"]
317 assert "def getdoc(obj)" in i["source"]
318
318
319 i = inspector.info(A.adder, detail_level=1)
319 i = inspector.info(A.adder, detail_level=1)
320 assert "def simple_add(a, b)" in i["source"]
320 assert "def simple_add(a, b)" in i["source"]
321
321
322
322
323 def test_property_docstring_is_in_info_for_detail_level_0():
323 def test_property_docstring_is_in_info_for_detail_level_0():
324 class A(object):
324 class A(object):
325 @property
325 @property
326 def foobar(self):
326 def foobar(self):
327 """This is `foobar` property."""
327 """This is `foobar` property."""
328 pass
328 pass
329
329
330 ip.user_ns["a_obj"] = A()
330 ip.user_ns["a_obj"] = A()
331 assert (
331 assert (
332 "This is `foobar` property."
332 "This is `foobar` property."
333 == ip.object_inspect("a_obj.foobar", detail_level=0)["docstring"]
333 == ip.object_inspect("a_obj.foobar", detail_level=0)["docstring"]
334 )
334 )
335
335
336 ip.user_ns["a_cls"] = A
336 ip.user_ns["a_cls"] = A
337 assert (
337 assert (
338 "This is `foobar` property."
338 "This is `foobar` property."
339 == ip.object_inspect("a_cls.foobar", detail_level=0)["docstring"]
339 == ip.object_inspect("a_cls.foobar", detail_level=0)["docstring"]
340 )
340 )
341
341
342
342
343 def test_pdef():
343 def test_pdef():
344 # See gh-1914
344 # See gh-1914
345 def foo(): pass
345 def foo(): pass
346 inspector.pdef(foo, 'foo')
346 inspector.pdef(foo, 'foo')
347
347
348
348
349 @contextmanager
349 @contextmanager
350 def cleanup_user_ns(**kwargs):
350 def cleanup_user_ns(**kwargs):
351 """
351 """
352 On exit delete all the keys that were not in user_ns before entering.
352 On exit delete all the keys that were not in user_ns before entering.
353
353
354 It does not restore old values !
354 It does not restore old values !
355
355
356 Parameters
356 Parameters
357 ----------
357 ----------
358
358
359 **kwargs
359 **kwargs
360 used to update ip.user_ns
360 used to update ip.user_ns
361
361
362 """
362 """
363 try:
363 try:
364 known = set(ip.user_ns.keys())
364 known = set(ip.user_ns.keys())
365 ip.user_ns.update(kwargs)
365 ip.user_ns.update(kwargs)
366 yield
366 yield
367 finally:
367 finally:
368 added = set(ip.user_ns.keys()) - known
368 added = set(ip.user_ns.keys()) - known
369 for k in added:
369 for k in added:
370 del ip.user_ns[k]
370 del ip.user_ns[k]
371
371
372
372
373 def test_pinfo_bool_raise():
373 def test_pinfo_bool_raise():
374 """
374 """
375 Test that bool method is not called on parent.
375 Test that bool method is not called on parent.
376 """
376 """
377
377
378 class RaiseBool:
378 class RaiseBool:
379 attr = None
379 attr = None
380
380
381 def __bool__(self):
381 def __bool__(self):
382 raise ValueError("pinfo should not access this method")
382 raise ValueError("pinfo should not access this method")
383
383
384 raise_bool = RaiseBool()
384 raise_bool = RaiseBool()
385
385
386 with cleanup_user_ns(raise_bool=raise_bool):
386 with cleanup_user_ns(raise_bool=raise_bool):
387 ip._inspect("pinfo", "raise_bool.attr", detail_level=0)
387 ip._inspect("pinfo", "raise_bool.attr", detail_level=0)
388
388
389
389
390 def test_pinfo_getindex():
390 def test_pinfo_getindex():
391 def dummy():
391 def dummy():
392 """
392 """
393 MARKER
393 MARKER
394 """
394 """
395
395
396 container = [dummy]
396 container = [dummy]
397 with cleanup_user_ns(container=container):
397 with cleanup_user_ns(container=container):
398 with AssertPrints("MARKER"):
398 with AssertPrints("MARKER"):
399 ip._inspect("pinfo", "container[0]", detail_level=0)
399 ip._inspect("pinfo", "container[0]", detail_level=0)
400 assert "container" not in ip.user_ns.keys()
400 assert "container" not in ip.user_ns.keys()
401
401
402
402
403 def test_qmark_getindex():
403 def test_qmark_getindex():
404 def dummy():
404 def dummy():
405 """
405 """
406 MARKER 2
406 MARKER 2
407 """
407 """
408
408
409 container = [dummy]
409 container = [dummy]
410 with cleanup_user_ns(container=container):
410 with cleanup_user_ns(container=container):
411 with AssertPrints("MARKER 2"):
411 with AssertPrints("MARKER 2"):
412 ip.run_cell("container[0]?")
412 ip.run_cell("container[0]?")
413 assert "container" not in ip.user_ns.keys()
413 assert "container" not in ip.user_ns.keys()
414
414
415
415
416 def test_qmark_getindex_negatif():
416 def test_qmark_getindex_negatif():
417 def dummy():
417 def dummy():
418 """
418 """
419 MARKER 3
419 MARKER 3
420 """
420 """
421
421
422 container = [dummy]
422 container = [dummy]
423 with cleanup_user_ns(container=container):
423 with cleanup_user_ns(container=container):
424 with AssertPrints("MARKER 3"):
424 with AssertPrints("MARKER 3"):
425 ip.run_cell("container[-1]?")
425 ip.run_cell("container[-1]?")
426 assert "container" not in ip.user_ns.keys()
426 assert "container" not in ip.user_ns.keys()
427
427
428
428
429
429
430 def test_pinfo_nonascii():
430 def test_pinfo_nonascii():
431 # See gh-1177
431 # See gh-1177
432 from . import nonascii2
432 from . import nonascii2
433 ip.user_ns['nonascii2'] = nonascii2
433 ip.user_ns['nonascii2'] = nonascii2
434 ip._inspect('pinfo', 'nonascii2', detail_level=1)
434 ip._inspect('pinfo', 'nonascii2', detail_level=1)
435
435
436 def test_pinfo_type():
436 def test_pinfo_type():
437 """
437 """
438 type can fail in various edge case, for example `type.__subclass__()`
438 type can fail in various edge case, for example `type.__subclass__()`
439 """
439 """
440 ip._inspect('pinfo', 'type')
440 ip._inspect('pinfo', 'type')
441
441
442
442
443 def test_pinfo_docstring_no_source():
443 def test_pinfo_docstring_no_source():
444 """Docstring should be included with detail_level=1 if there is no source"""
444 """Docstring should be included with detail_level=1 if there is no source"""
445 with AssertPrints('Docstring:'):
445 with AssertPrints('Docstring:'):
446 ip._inspect('pinfo', 'str.format', detail_level=0)
446 ip._inspect('pinfo', 'str.format', detail_level=0)
447 with AssertPrints('Docstring:'):
447 with AssertPrints('Docstring:'):
448 ip._inspect('pinfo', 'str.format', detail_level=1)
448 ip._inspect('pinfo', 'str.format', detail_level=1)
449
449
450
450
451 def test_pinfo_no_docstring_if_source():
451 def test_pinfo_no_docstring_if_source():
452 """Docstring should not be included with detail_level=1 if source is found"""
452 """Docstring should not be included with detail_level=1 if source is found"""
453 def foo():
453 def foo():
454 """foo has a docstring"""
454 """foo has a docstring"""
455
455
456 ip.user_ns['foo'] = foo
456 ip.user_ns['foo'] = foo
457
457
458 with AssertPrints('Docstring:'):
458 with AssertPrints('Docstring:'):
459 ip._inspect('pinfo', 'foo', detail_level=0)
459 ip._inspect('pinfo', 'foo', detail_level=0)
460 with AssertPrints('Source:'):
460 with AssertPrints('Source:'):
461 ip._inspect('pinfo', 'foo', detail_level=1)
461 ip._inspect('pinfo', 'foo', detail_level=1)
462 with AssertNotPrints('Docstring:'):
462 with AssertNotPrints('Docstring:'):
463 ip._inspect('pinfo', 'foo', detail_level=1)
463 ip._inspect('pinfo', 'foo', detail_level=1)
464
464
465
465
466 def test_pinfo_docstring_if_detail_and_no_source():
466 def test_pinfo_docstring_if_detail_and_no_source():
467 """ Docstring should be displayed if source info not available """
467 """ Docstring should be displayed if source info not available """
468 obj_def = '''class Foo(object):
468 obj_def = '''class Foo(object):
469 """ This is a docstring for Foo """
469 """ This is a docstring for Foo """
470 def bar(self):
470 def bar(self):
471 """ This is a docstring for Foo.bar """
471 """ This is a docstring for Foo.bar """
472 pass
472 pass
473 '''
473 '''
474
474
475 ip.run_cell(obj_def)
475 ip.run_cell(obj_def)
476 ip.run_cell('foo = Foo()')
476 ip.run_cell('foo = Foo()')
477
477
478 with AssertNotPrints("Source:"):
478 with AssertNotPrints("Source:"):
479 with AssertPrints('Docstring:'):
479 with AssertPrints('Docstring:'):
480 ip._inspect('pinfo', 'foo', detail_level=0)
480 ip._inspect('pinfo', 'foo', detail_level=0)
481 with AssertPrints('Docstring:'):
481 with AssertPrints('Docstring:'):
482 ip._inspect('pinfo', 'foo', detail_level=1)
482 ip._inspect('pinfo', 'foo', detail_level=1)
483 with AssertPrints('Docstring:'):
483 with AssertPrints('Docstring:'):
484 ip._inspect('pinfo', 'foo.bar', detail_level=0)
484 ip._inspect('pinfo', 'foo.bar', detail_level=0)
485
485
486 with AssertNotPrints('Docstring:'):
486 with AssertNotPrints('Docstring:'):
487 with AssertPrints('Source:'):
487 with AssertPrints('Source:'):
488 ip._inspect('pinfo', 'foo.bar', detail_level=1)
488 ip._inspect('pinfo', 'foo.bar', detail_level=1)
489
489
490
490
491 def test_pinfo_docstring_dynamic():
491 @pytest.mark.xfail(
492 sys.version_info.releaselevel not in ("final", "candidate"),
493 reason="fails on 3.13.dev",
494 strict=True,
495 )
496 def test_pinfo_docstring_dynamic(capsys):
492 obj_def = """class Bar:
497 obj_def = """class Bar:
493 __custom_documentations__ = {
498 __custom_documentations__ = {
494 "prop" : "cdoc for prop",
499 "prop" : "cdoc for prop",
495 "non_exist" : "cdoc for non_exist",
500 "non_exist" : "cdoc for non_exist",
496 }
501 }
497 @property
502 @property
498 def prop(self):
503 def prop(self):
499 '''
504 '''
500 Docstring for prop
505 Docstring for prop
501 '''
506 '''
502 return self._prop
507 return self._prop
503
508
504 @prop.setter
509 @prop.setter
505 def prop(self, v):
510 def prop(self, v):
506 self._prop = v
511 self._prop = v
507 """
512 """
508 ip.run_cell(obj_def)
513 ip.run_cell(obj_def)
509
514
510 ip.run_cell("b = Bar()")
515 ip.run_cell("b = Bar()")
511
516
512 with AssertPrints("Docstring: cdoc for prop"):
517 ip.run_line_magic("pinfo", "b.prop")
513 ip.run_line_magic("pinfo", "b.prop")
518 captured = capsys.readouterr()
519 assert "Docstring: cdoc for prop" in captured.out
514
520
515 with AssertPrints("Docstring: cdoc for non_exist"):
521 ip.run_line_magic("pinfo", "b.non_exist")
516 ip.run_line_magic("pinfo", "b.non_exist")
522 captured = capsys.readouterr()
523 assert "Docstring: cdoc for non_exist" in captured.out
517
524
518 with AssertPrints("Docstring: cdoc for prop"):
525 ip.run_cell("b.prop?")
519 ip.run_cell("b.prop?")
526 captured = capsys.readouterr()
527 assert "Docstring: cdoc for prop" in captured.out
520
528
521 with AssertPrints("Docstring: cdoc for non_exist"):
529 ip.run_cell("b.non_exist?")
522 ip.run_cell("b.non_exist?")
530 captured = capsys.readouterr()
531 assert "Docstring: cdoc for non_exist" in captured.out
523
532
524 with AssertPrints("Docstring: <no docstring>"):
533 ip.run_cell("b.undefined?")
525 ip.run_cell("b.undefined?")
534 captured = capsys.readouterr()
535 assert "Docstring: <no docstring>" in captured.out
526
536
527
537
528 def test_pinfo_magic():
538 def test_pinfo_magic():
529 with AssertPrints("Docstring:"):
539 with AssertPrints("Docstring:"):
530 ip._inspect("pinfo", "lsmagic", detail_level=0)
540 ip._inspect("pinfo", "lsmagic", detail_level=0)
531
541
532 with AssertPrints("Source:"):
542 with AssertPrints("Source:"):
533 ip._inspect("pinfo", "lsmagic", detail_level=1)
543 ip._inspect("pinfo", "lsmagic", detail_level=1)
534
544
535
545
536 def test_init_colors():
546 def test_init_colors():
537 # ensure colors are not present in signature info
547 # ensure colors are not present in signature info
538 info = inspector.info(HasSignature)
548 info = inspector.info(HasSignature)
539 init_def = info["init_definition"]
549 init_def = info["init_definition"]
540 assert "[0m" not in init_def
550 assert "[0m" not in init_def
541
551
542
552
543 def test_builtin_init():
553 def test_builtin_init():
544 info = inspector.info(list)
554 info = inspector.info(list)
545 init_def = info['init_definition']
555 init_def = info['init_definition']
546 assert init_def is not None
556 assert init_def is not None
547
557
548
558
549 def test_render_signature_short():
559 def test_render_signature_short():
550 def short_fun(a=1): pass
560 def short_fun(a=1): pass
551 sig = oinspect._render_signature(
561 sig = oinspect._render_signature(
552 signature(short_fun),
562 signature(short_fun),
553 short_fun.__name__,
563 short_fun.__name__,
554 )
564 )
555 assert sig == "short_fun(a=1)"
565 assert sig == "short_fun(a=1)"
556
566
557
567
558 def test_render_signature_long():
568 def test_render_signature_long():
559 from typing import Optional
569 from typing import Optional
560
570
561 def long_function(
571 def long_function(
562 a_really_long_parameter: int,
572 a_really_long_parameter: int,
563 and_another_long_one: bool = False,
573 and_another_long_one: bool = False,
564 let_us_make_sure_this_is_looong: Optional[str] = None,
574 let_us_make_sure_this_is_looong: Optional[str] = None,
565 ) -> bool: pass
575 ) -> bool: pass
566
576
567 sig = oinspect._render_signature(
577 sig = oinspect._render_signature(
568 signature(long_function),
578 signature(long_function),
569 long_function.__name__,
579 long_function.__name__,
570 )
580 )
571 expected = """\
581 expected = """\
572 long_function(
582 long_function(
573 a_really_long_parameter: int,
583 a_really_long_parameter: int,
574 and_another_long_one: bool = False,
584 and_another_long_one: bool = False,
575 let_us_make_sure_this_is_looong: Optional[str] = None,
585 let_us_make_sure_this_is_looong: Optional[str] = None,
576 ) -> bool\
586 ) -> bool\
577 """
587 """
578
588
579 assert sig == expected
589 assert sig == expected
@@ -1,368 +1,369
1 [build-system]
1 [build-system]
2 requires = ["setuptools>=61.2"]
2 requires = ["setuptools>=61.2"]
3 # We need access to the 'setupbase' module at build time.
3 # We need access to the 'setupbase' module at build time.
4 # Hence we declare a custom build backend.
4 # Hence we declare a custom build backend.
5 build-backend = "_build_meta" # just re-exports setuptools.build_meta definitions
5 build-backend = "_build_meta" # just re-exports setuptools.build_meta definitions
6 backend-path = ["."]
6 backend-path = ["."]
7
7
8 [project]
8 [project]
9 name = "ipython"
9 name = "ipython"
10 description = "IPython: Productive Interactive Computing"
10 description = "IPython: Productive Interactive Computing"
11 keywords = ["Interactive", "Interpreter", "Shell", "Embedding"]
11 keywords = ["Interactive", "Interpreter", "Shell", "Embedding"]
12 classifiers = [
12 classifiers = [
13 "Framework :: IPython",
13 "Framework :: IPython",
14 "Framework :: Jupyter",
14 "Framework :: Jupyter",
15 "Intended Audience :: Developers",
15 "Intended Audience :: Developers",
16 "Intended Audience :: Science/Research",
16 "Intended Audience :: Science/Research",
17 "License :: OSI Approved :: BSD License",
17 "License :: OSI Approved :: BSD License",
18 "Programming Language :: Python",
18 "Programming Language :: Python",
19 "Programming Language :: Python :: 3",
19 "Programming Language :: Python :: 3",
20 "Programming Language :: Python :: 3 :: Only",
20 "Programming Language :: Python :: 3 :: Only",
21 "Topic :: System :: Shells",
21 "Topic :: System :: Shells",
22 ]
22 ]
23 requires-python = ">=3.10"
23 requires-python = ">=3.10"
24 dependencies = [
24 dependencies = [
25 'colorama; sys_platform == "win32"',
25 'colorama; sys_platform == "win32"',
26 "decorator",
26 "decorator",
27 "exceptiongroup; python_version<'3.11'",
27 "exceptiongroup; python_version<'3.11'",
28 "jedi>=0.16",
28 "jedi>=0.16",
29 "matplotlib-inline",
29 "matplotlib-inline",
30 'pexpect>4.3; sys_platform != "win32" and sys_platform != "emscripten"',
30 'pexpect>4.3; sys_platform != "win32" and sys_platform != "emscripten"',
31 "prompt_toolkit>=3.0.41,<3.1.0",
31 "prompt_toolkit>=3.0.41,<3.1.0",
32 "pygments>=2.4.0",
32 "pygments>=2.4.0",
33 "stack_data",
33 "stack_data",
34 "traitlets>=5.13.0",
34 "traitlets>=5.13.0",
35 "typing_extensions>=4.6; python_version<'3.12'",
35 "typing_extensions>=4.6; python_version<'3.12'",
36 ]
36 ]
37 dynamic = ["authors", "license", "version"]
37 dynamic = ["authors", "license", "version"]
38
38
39 [project.entry-points."pygments.lexers"]
39 [project.entry-points."pygments.lexers"]
40 ipythonconsole = "IPython.lib.lexers:IPythonConsoleLexer"
40 ipythonconsole = "IPython.lib.lexers:IPythonConsoleLexer"
41 ipython = "IPython.lib.lexers:IPythonLexer"
41 ipython = "IPython.lib.lexers:IPythonLexer"
42 ipython3 = "IPython.lib.lexers:IPython3Lexer"
42 ipython3 = "IPython.lib.lexers:IPython3Lexer"
43
43
44 [project.scripts]
44 [project.scripts]
45 ipython = "IPython:start_ipython"
45 ipython = "IPython:start_ipython"
46 ipython3 = "IPython:start_ipython"
46 ipython3 = "IPython:start_ipython"
47
47
48 [project.readme]
48 [project.readme]
49 file = "long_description.rst"
49 file = "long_description.rst"
50 content-type = "text/x-rst"
50 content-type = "text/x-rst"
51
51
52 [project.urls]
52 [project.urls]
53 Homepage = "https://ipython.org"
53 Homepage = "https://ipython.org"
54 Documentation = "https://ipython.readthedocs.io/"
54 Documentation = "https://ipython.readthedocs.io/"
55 Funding = "https://numfocus.org/"
55 Funding = "https://numfocus.org/"
56 Source = "https://github.com/ipython/ipython"
56 Source = "https://github.com/ipython/ipython"
57 Tracker = "https://github.com/ipython/ipython/issues"
57 Tracker = "https://github.com/ipython/ipython/issues"
58
58
59 [project.optional-dependencies]
59 [project.optional-dependencies]
60 black = [
60 black = [
61 "black",
61 "black",
62 ]
62 ]
63 doc = [
63 doc = [
64 "docrepr",
64 "docrepr",
65 "exceptiongroup",
65 "exceptiongroup",
66 "intersphinx_registry",
66 "intersphinx_registry",
67 "ipykernel",
67 "ipykernel",
68 "ipython[test]",
68 "ipython[test]",
69 "matplotlib",
69 "matplotlib",
70 "setuptools>=18.5",
70 "setuptools>=18.5",
71 "sphinx-rtd-theme",
71 "sphinx-rtd-theme",
72 "sphinx>=1.3",
72 "sphinx>=1.3",
73 "sphinxcontrib-jquery",
73 "sphinxcontrib-jquery",
74 "tomli ; python_version<'3.11'",
74 "tomli ; python_version<'3.11'",
75 "typing_extensions",
75 "typing_extensions",
76 ]
76 ]
77 kernel = [
77 kernel = [
78 "ipykernel",
78 "ipykernel",
79 ]
79 ]
80 nbconvert = [
80 nbconvert = [
81 "nbconvert",
81 "nbconvert",
82 ]
82 ]
83 nbformat = [
83 nbformat = [
84 "nbformat",
84 "nbformat",
85 ]
85 ]
86 notebook = [
86 notebook = [
87 "ipywidgets",
87 "ipywidgets",
88 "notebook",
88 "notebook",
89 ]
89 ]
90 parallel = [
90 parallel = [
91 "ipyparallel",
91 "ipyparallel",
92 ]
92 ]
93 qtconsole = [
93 qtconsole = [
94 "qtconsole",
94 "qtconsole",
95 ]
95 ]
96 terminal = []
96 terminal = []
97 test = [
97 test = [
98 "pytest",
98 "pytest",
99 "pytest-asyncio<0.22",
99 "pytest-asyncio<0.22",
100 "testpath",
100 "testpath",
101 "pickleshare",
101 "pickleshare",
102 "packaging",
102 ]
103 ]
103 test_extra = [
104 test_extra = [
104 "ipython[test]",
105 "ipython[test]",
105 "curio",
106 "curio",
106 "matplotlib!=3.2.0",
107 "matplotlib!=3.2.0",
107 "nbformat",
108 "nbformat",
108 "numpy>=1.23",
109 "numpy>=1.23",
109 "pandas",
110 "pandas",
110 "trio",
111 "trio",
111 ]
112 ]
112 matplotlib = [
113 matplotlib = [
113 "matplotlib"
114 "matplotlib"
114 ]
115 ]
115 all = [
116 all = [
116 "ipython[black,doc,kernel,nbconvert,nbformat,notebook,parallel,qtconsole,matplotlib]",
117 "ipython[black,doc,kernel,nbconvert,nbformat,notebook,parallel,qtconsole,matplotlib]",
117 "ipython[test,test_extra]",
118 "ipython[test,test_extra]",
118 ]
119 ]
119
120
120 [tool.mypy]
121 [tool.mypy]
121 python_version = "3.10"
122 python_version = "3.10"
122 ignore_missing_imports = true
123 ignore_missing_imports = true
123 follow_imports = 'silent'
124 follow_imports = 'silent'
124 exclude = [
125 exclude = [
125 'test_\.+\.py',
126 'test_\.+\.py',
126 'IPython.utils.tests.test_wildcard',
127 'IPython.utils.tests.test_wildcard',
127 'testing',
128 'testing',
128 'tests',
129 'tests',
129 'PyColorize.py',
130 'PyColorize.py',
130 '_process_win32_controller.py',
131 '_process_win32_controller.py',
131 'IPython/core/application.py',
132 'IPython/core/application.py',
132 'IPython/core/profileapp.py',
133 'IPython/core/profileapp.py',
133 'IPython/lib/deepreload.py',
134 'IPython/lib/deepreload.py',
134 'IPython/sphinxext/ipython_directive.py',
135 'IPython/sphinxext/ipython_directive.py',
135 'IPython/terminal/ipapp.py',
136 'IPython/terminal/ipapp.py',
136 'IPython/utils/_process_win32.py',
137 'IPython/utils/_process_win32.py',
137 'IPython/utils/path.py',
138 'IPython/utils/path.py',
138 ]
139 ]
139 disallow_untyped_defs = true
140 disallow_untyped_defs = true
140 # ignore_errors = false
141 # ignore_errors = false
141 # ignore_missing_imports = false
142 # ignore_missing_imports = false
142 # disallow_untyped_calls = true
143 # disallow_untyped_calls = true
143 disallow_incomplete_defs = true
144 disallow_incomplete_defs = true
144 # check_untyped_defs = true
145 # check_untyped_defs = true
145 # disallow_untyped_decorators = true
146 # disallow_untyped_decorators = true
146 warn_redundant_casts = true
147 warn_redundant_casts = true
147
148
148 [[tool.mypy.overrides]]
149 [[tool.mypy.overrides]]
149 module = [
150 module = [
150 "IPython.utils.text",
151 "IPython.utils.text",
151 ]
152 ]
152 disallow_untyped_defs = false
153 disallow_untyped_defs = false
153 check_untyped_defs = false
154 check_untyped_defs = false
154 disallow_untyped_decorators = false
155 disallow_untyped_decorators = false
155
156
156
157
157 # gloabl ignore error
158 # gloabl ignore error
158 [[tool.mypy.overrides]]
159 [[tool.mypy.overrides]]
159 module = [
160 module = [
160 "IPython",
161 "IPython",
161 "IPython.conftest",
162 "IPython.conftest",
162 "IPython.core.alias",
163 "IPython.core.alias",
163 "IPython.core.async_helpers",
164 "IPython.core.async_helpers",
164 "IPython.core.autocall",
165 "IPython.core.autocall",
165 "IPython.core.builtin_trap",
166 "IPython.core.builtin_trap",
166 "IPython.core.compilerop",
167 "IPython.core.compilerop",
167 "IPython.core.completer",
168 "IPython.core.completer",
168 "IPython.core.completerlib",
169 "IPython.core.completerlib",
169 "IPython.core.crashhandler",
170 "IPython.core.crashhandler",
170 "IPython.core.debugger",
171 "IPython.core.debugger",
171 "IPython.core.display",
172 "IPython.core.display",
172 "IPython.core.display_functions",
173 "IPython.core.display_functions",
173 "IPython.core.display_trap",
174 "IPython.core.display_trap",
174 "IPython.core.displayhook",
175 "IPython.core.displayhook",
175 "IPython.core.displaypub",
176 "IPython.core.displaypub",
176 "IPython.core.events",
177 "IPython.core.events",
177 "IPython.core.excolors",
178 "IPython.core.excolors",
178 "IPython.core.extensions",
179 "IPython.core.extensions",
179 "IPython.core.formatters",
180 "IPython.core.formatters",
180 "IPython.core.getipython",
181 "IPython.core.getipython",
181 "IPython.core.guarded_eval",
182 "IPython.core.guarded_eval",
182 "IPython.core.history",
183 "IPython.core.history",
183 "IPython.core.historyapp",
184 "IPython.core.historyapp",
184 "IPython.core.hooks",
185 "IPython.core.hooks",
185 "IPython.core.inputsplitter",
186 "IPython.core.inputsplitter",
186 "IPython.core.inputtransformer",
187 "IPython.core.inputtransformer",
187 "IPython.core.inputtransformer2",
188 "IPython.core.inputtransformer2",
188 "IPython.core.interactiveshell",
189 "IPython.core.interactiveshell",
189 "IPython.core.logger",
190 "IPython.core.logger",
190 "IPython.core.macro",
191 "IPython.core.macro",
191 "IPython.core.magic",
192 "IPython.core.magic",
192 "IPython.core.magic_arguments",
193 "IPython.core.magic_arguments",
193 "IPython.core.magics.ast_mod",
194 "IPython.core.magics.ast_mod",
194 "IPython.core.magics.auto",
195 "IPython.core.magics.auto",
195 "IPython.core.magics.basic",
196 "IPython.core.magics.basic",
196 "IPython.core.magics.code",
197 "IPython.core.magics.code",
197 "IPython.core.magics.config",
198 "IPython.core.magics.config",
198 "IPython.core.magics.display",
199 "IPython.core.magics.display",
199 "IPython.core.magics.execution",
200 "IPython.core.magics.execution",
200 "IPython.core.magics.extension",
201 "IPython.core.magics.extension",
201 "IPython.core.magics.history",
202 "IPython.core.magics.history",
202 "IPython.core.magics.logging",
203 "IPython.core.magics.logging",
203 "IPython.core.magics.namespace",
204 "IPython.core.magics.namespace",
204 "IPython.core.magics.osm",
205 "IPython.core.magics.osm",
205 "IPython.core.magics.packaging",
206 "IPython.core.magics.packaging",
206 "IPython.core.magics.pylab",
207 "IPython.core.magics.pylab",
207 "IPython.core.magics.script",
208 "IPython.core.magics.script",
208 "IPython.core.oinspect",
209 "IPython.core.oinspect",
209 "IPython.core.page",
210 "IPython.core.page",
210 "IPython.core.payload",
211 "IPython.core.payload",
211 "IPython.core.payloadpage",
212 "IPython.core.payloadpage",
212 "IPython.core.prefilter",
213 "IPython.core.prefilter",
213 "IPython.core.profiledir",
214 "IPython.core.profiledir",
214 "IPython.core.prompts",
215 "IPython.core.prompts",
215 "IPython.core.pylabtools",
216 "IPython.core.pylabtools",
216 "IPython.core.shellapp",
217 "IPython.core.shellapp",
217 "IPython.core.splitinput",
218 "IPython.core.splitinput",
218 "IPython.core.ultratb",
219 "IPython.core.ultratb",
219 "IPython.extensions.autoreload",
220 "IPython.extensions.autoreload",
220 "IPython.extensions.storemagic",
221 "IPython.extensions.storemagic",
221 "IPython.external.qt_for_kernel",
222 "IPython.external.qt_for_kernel",
222 "IPython.external.qt_loaders",
223 "IPython.external.qt_loaders",
223 "IPython.lib.backgroundjobs",
224 "IPython.lib.backgroundjobs",
224 "IPython.lib.clipboard",
225 "IPython.lib.clipboard",
225 "IPython.lib.demo",
226 "IPython.lib.demo",
226 "IPython.lib.display",
227 "IPython.lib.display",
227 "IPython.lib.editorhooks",
228 "IPython.lib.editorhooks",
228 "IPython.lib.guisupport",
229 "IPython.lib.guisupport",
229 "IPython.lib.latextools",
230 "IPython.lib.latextools",
230 "IPython.lib.lexers",
231 "IPython.lib.lexers",
231 "IPython.lib.pretty",
232 "IPython.lib.pretty",
232 "IPython.paths",
233 "IPython.paths",
233 "IPython.sphinxext.ipython_console_highlighting",
234 "IPython.sphinxext.ipython_console_highlighting",
234 "IPython.terminal.debugger",
235 "IPython.terminal.debugger",
235 "IPython.terminal.embed",
236 "IPython.terminal.embed",
236 "IPython.terminal.interactiveshell",
237 "IPython.terminal.interactiveshell",
237 "IPython.terminal.magics",
238 "IPython.terminal.magics",
238 "IPython.terminal.prompts",
239 "IPython.terminal.prompts",
239 "IPython.terminal.pt_inputhooks",
240 "IPython.terminal.pt_inputhooks",
240 "IPython.terminal.pt_inputhooks.asyncio",
241 "IPython.terminal.pt_inputhooks.asyncio",
241 "IPython.terminal.pt_inputhooks.glut",
242 "IPython.terminal.pt_inputhooks.glut",
242 "IPython.terminal.pt_inputhooks.gtk",
243 "IPython.terminal.pt_inputhooks.gtk",
243 "IPython.terminal.pt_inputhooks.gtk3",
244 "IPython.terminal.pt_inputhooks.gtk3",
244 "IPython.terminal.pt_inputhooks.gtk4",
245 "IPython.terminal.pt_inputhooks.gtk4",
245 "IPython.terminal.pt_inputhooks.osx",
246 "IPython.terminal.pt_inputhooks.osx",
246 "IPython.terminal.pt_inputhooks.pyglet",
247 "IPython.terminal.pt_inputhooks.pyglet",
247 "IPython.terminal.pt_inputhooks.qt",
248 "IPython.terminal.pt_inputhooks.qt",
248 "IPython.terminal.pt_inputhooks.tk",
249 "IPython.terminal.pt_inputhooks.tk",
249 "IPython.terminal.pt_inputhooks.wx",
250 "IPython.terminal.pt_inputhooks.wx",
250 "IPython.terminal.ptutils",
251 "IPython.terminal.ptutils",
251 "IPython.terminal.shortcuts",
252 "IPython.terminal.shortcuts",
252 "IPython.terminal.shortcuts.auto_match",
253 "IPython.terminal.shortcuts.auto_match",
253 "IPython.terminal.shortcuts.auto_suggest",
254 "IPython.terminal.shortcuts.auto_suggest",
254 "IPython.terminal.shortcuts.filters",
255 "IPython.terminal.shortcuts.filters",
255 "IPython.utils._process_cli",
256 "IPython.utils._process_cli",
256 "IPython.utils._process_common",
257 "IPython.utils._process_common",
257 "IPython.utils._process_emscripten",
258 "IPython.utils._process_emscripten",
258 "IPython.utils._process_posix",
259 "IPython.utils._process_posix",
259 "IPython.utils.capture",
260 "IPython.utils.capture",
260 "IPython.utils.coloransi",
261 "IPython.utils.coloransi",
261 "IPython.utils.contexts",
262 "IPython.utils.contexts",
262 "IPython.utils.data",
263 "IPython.utils.data",
263 "IPython.utils.decorators",
264 "IPython.utils.decorators",
264 "IPython.utils.dir2",
265 "IPython.utils.dir2",
265 "IPython.utils.encoding",
266 "IPython.utils.encoding",
266 "IPython.utils.frame",
267 "IPython.utils.frame",
267 "IPython.utils.generics",
268 "IPython.utils.generics",
268 "IPython.utils.importstring",
269 "IPython.utils.importstring",
269 "IPython.utils.io",
270 "IPython.utils.io",
270 "IPython.utils.ipstruct",
271 "IPython.utils.ipstruct",
271 "IPython.utils.module_paths",
272 "IPython.utils.module_paths",
272 "IPython.utils.openpy",
273 "IPython.utils.openpy",
273 "IPython.utils.process",
274 "IPython.utils.process",
274 "IPython.utils.py3compat",
275 "IPython.utils.py3compat",
275 "IPython.utils.sentinel",
276 "IPython.utils.sentinel",
276 "IPython.utils.shimmodule",
277 "IPython.utils.shimmodule",
277 "IPython.utils.strdispatch",
278 "IPython.utils.strdispatch",
278 "IPython.utils.sysinfo",
279 "IPython.utils.sysinfo",
279 "IPython.utils.syspathcontext",
280 "IPython.utils.syspathcontext",
280 "IPython.utils.tempdir",
281 "IPython.utils.tempdir",
281 "IPython.utils.terminal",
282 "IPython.utils.terminal",
282 "IPython.utils.timing",
283 "IPython.utils.timing",
283 "IPython.utils.tokenutil",
284 "IPython.utils.tokenutil",
284 "IPython.utils.tz",
285 "IPython.utils.tz",
285 "IPython.utils.ulinecache",
286 "IPython.utils.ulinecache",
286 "IPython.utils.version",
287 "IPython.utils.version",
287 "IPython.utils.wildcard",
288 "IPython.utils.wildcard",
288
289
289 ]
290 ]
290 disallow_untyped_defs = false
291 disallow_untyped_defs = false
291 ignore_errors = true
292 ignore_errors = true
292 ignore_missing_imports = true
293 ignore_missing_imports = true
293 disallow_untyped_calls = false
294 disallow_untyped_calls = false
294 disallow_incomplete_defs = false
295 disallow_incomplete_defs = false
295 check_untyped_defs = false
296 check_untyped_defs = false
296 disallow_untyped_decorators = false
297 disallow_untyped_decorators = false
297
298
298 [tool.pytest.ini_options]
299 [tool.pytest.ini_options]
299 addopts = [
300 addopts = [
300 "--durations=10",
301 "--durations=10",
301 "-pIPython.testing.plugin.pytest_ipdoctest",
302 "-pIPython.testing.plugin.pytest_ipdoctest",
302 "--ipdoctest-modules",
303 "--ipdoctest-modules",
303 "--ignore=docs",
304 "--ignore=docs",
304 "--ignore=examples",
305 "--ignore=examples",
305 "--ignore=htmlcov",
306 "--ignore=htmlcov",
306 "--ignore=ipython_kernel",
307 "--ignore=ipython_kernel",
307 "--ignore=ipython_parallel",
308 "--ignore=ipython_parallel",
308 "--ignore=results",
309 "--ignore=results",
309 "--ignore=tmp",
310 "--ignore=tmp",
310 "--ignore=tools",
311 "--ignore=tools",
311 "--ignore=traitlets",
312 "--ignore=traitlets",
312 "--ignore=IPython/core/tests/daft_extension",
313 "--ignore=IPython/core/tests/daft_extension",
313 "--ignore=IPython/sphinxext",
314 "--ignore=IPython/sphinxext",
314 "--ignore=IPython/terminal/pt_inputhooks",
315 "--ignore=IPython/terminal/pt_inputhooks",
315 "--ignore=IPython/__main__.py",
316 "--ignore=IPython/__main__.py",
316 "--ignore=IPython/external/qt_for_kernel.py",
317 "--ignore=IPython/external/qt_for_kernel.py",
317 "--ignore=IPython/html/widgets/widget_link.py",
318 "--ignore=IPython/html/widgets/widget_link.py",
318 "--ignore=IPython/html/widgets/widget_output.py",
319 "--ignore=IPython/html/widgets/widget_output.py",
319 "--ignore=IPython/terminal/console.py",
320 "--ignore=IPython/terminal/console.py",
320 "--ignore=IPython/utils/_process_cli.py",
321 "--ignore=IPython/utils/_process_cli.py",
321 "--ignore=IPython/utils/_process_posix.py",
322 "--ignore=IPython/utils/_process_posix.py",
322 "--ignore=IPython/utils/_process_win32.py",
323 "--ignore=IPython/utils/_process_win32.py",
323 "--ignore=IPython/utils/_process_win32_controller.py",
324 "--ignore=IPython/utils/_process_win32_controller.py",
324 "--ignore=IPython/utils/daemonize.py",
325 "--ignore=IPython/utils/daemonize.py",
325 "--ignore=IPython/utils/eventful.py",
326 "--ignore=IPython/utils/eventful.py",
326 "--ignore=IPython/kernel",
327 "--ignore=IPython/kernel",
327 "--ignore=IPython/consoleapp.py",
328 "--ignore=IPython/consoleapp.py",
328 "--ignore=IPython/core/inputsplitter.py",
329 "--ignore=IPython/core/inputsplitter.py",
329 "--ignore=IPython/lib/kernel.py",
330 "--ignore=IPython/lib/kernel.py",
330 "--ignore=IPython/utils/jsonutil.py",
331 "--ignore=IPython/utils/jsonutil.py",
331 "--ignore=IPython/utils/localinterfaces.py",
332 "--ignore=IPython/utils/localinterfaces.py",
332 "--ignore=IPython/utils/log.py",
333 "--ignore=IPython/utils/log.py",
333 "--ignore=IPython/utils/signatures.py",
334 "--ignore=IPython/utils/signatures.py",
334 "--ignore=IPython/utils/traitlets.py",
335 "--ignore=IPython/utils/traitlets.py",
335 "--ignore=IPython/utils/version.py"
336 "--ignore=IPython/utils/version.py"
336 ]
337 ]
337 doctest_optionflags = [
338 doctest_optionflags = [
338 "NORMALIZE_WHITESPACE",
339 "NORMALIZE_WHITESPACE",
339 "ELLIPSIS"
340 "ELLIPSIS"
340 ]
341 ]
341 ipdoctest_optionflags = [
342 ipdoctest_optionflags = [
342 "NORMALIZE_WHITESPACE",
343 "NORMALIZE_WHITESPACE",
343 "ELLIPSIS"
344 "ELLIPSIS"
344 ]
345 ]
345 asyncio_mode = "strict"
346 asyncio_mode = "strict"
346
347
347 [tool.pyright]
348 [tool.pyright]
348 pythonPlatform="All"
349 pythonPlatform="All"
349
350
350 [tool.setuptools]
351 [tool.setuptools]
351 zip-safe = false
352 zip-safe = false
352 platforms = ["Linux", "Mac OSX", "Windows"]
353 platforms = ["Linux", "Mac OSX", "Windows"]
353 license-files = ["LICENSE"]
354 license-files = ["LICENSE"]
354 include-package-data = false
355 include-package-data = false
355
356
356 [tool.setuptools.packages.find]
357 [tool.setuptools.packages.find]
357 exclude = ["setupext"]
358 exclude = ["setupext"]
358 namespaces = false
359 namespaces = false
359
360
360 [tool.setuptools.package-data]
361 [tool.setuptools.package-data]
361 "IPython" = ["py.typed"]
362 "IPython" = ["py.typed"]
362 "IPython.core" = ["profile/README*"]
363 "IPython.core" = ["profile/README*"]
363 "IPython.core.tests" = ["*.png", "*.jpg", "daft_extension/*.py"]
364 "IPython.core.tests" = ["*.png", "*.jpg", "daft_extension/*.py"]
364 "IPython.lib.tests" = ["*.wav"]
365 "IPython.lib.tests" = ["*.wav"]
365 "IPython.testing.plugin" = ["*.txt"]
366 "IPython.testing.plugin" = ["*.txt"]
366
367
367 [tool.setuptools.dynamic]
368 [tool.setuptools.dynamic]
368 version = {attr = "IPython.core.release.__version__"}
369 version = {attr = "IPython.core.release.__version__"}
General Comments 0
You need to be logged in to leave comments. Login now