##// END OF EJS Templates
stricted but no doctests
M Bussonnier -
Show More
@@ -1,1749 +1,1757
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"), reason="Known failure on jedi<=0.18.0"
542 )
535 def test_deduplicate_completions(self):
543 def test_deduplicate_completions(self):
536 """
544 """
537 Test that completions are correctly deduplicated (even if ranges are not the same)
545 Test that completions are correctly deduplicated (even if ranges are not the same)
538 """
546 """
539 ip = get_ipython()
547 ip = get_ipython()
540 ip.ex(
548 ip.ex(
541 textwrap.dedent(
549 textwrap.dedent(
542 """
550 """
543 class Z:
551 class Z:
544 zoo = 1
552 zoo = 1
545 """
553 """
546 )
554 )
547 )
555 )
548 with provisionalcompleter():
556 with provisionalcompleter():
549 ip.Completer.use_jedi = True
557 ip.Completer.use_jedi = True
550 l = list(
558 l = list(
551 _deduplicate_completions("Z.z", ip.Completer.completions("Z.z", 3))
559 _deduplicate_completions("Z.z", ip.Completer.completions("Z.z", 3))
552 )
560 )
553 ip.Completer.use_jedi = False
561 ip.Completer.use_jedi = False
554
562
555 assert len(l) == 1, "Completions (Z.z<tab>) correctly deduplicate: %s " % l
563 assert len(l) == 1, "Completions (Z.z<tab>) correctly deduplicate: %s " % l
556 assert l[0].text == "zoo" # and not `it.accumulate`
564 assert l[0].text == "zoo" # and not `it.accumulate`
557
565
558 @pytest.mark.xfail(
566 @pytest.mark.xfail(
559 sys.version_info.releaselevel in ("alpha",),
567 sys.version_info.releaselevel in ("alpha",),
560 reason="Parso does not yet parse 3.13",
568 reason="Parso does not yet parse 3.13",
561 )
569 )
562 def test_greedy_completions(self):
570 def test_greedy_completions(self):
563 """
571 """
564 Test the capability of the Greedy completer.
572 Test the capability of the Greedy completer.
565
573
566 Most of the test here does not really show off the greedy completer, for proof
574 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.
575 each of the text below now pass with Jedi. The greedy completer is capable of more.
568
576
569 See the :any:`test_dict_key_completion_contexts`
577 See the :any:`test_dict_key_completion_contexts`
570
578
571 """
579 """
572 ip = get_ipython()
580 ip = get_ipython()
573 ip.ex("a=list(range(5))")
581 ip.ex("a=list(range(5))")
574 ip.ex("d = {'a b': str}")
582 ip.ex("d = {'a b': str}")
575 _, c = ip.complete(".", line="a[0].")
583 _, c = ip.complete(".", line="a[0].")
576 self.assertFalse(".real" in c, "Shouldn't have completed on a[0]: %s" % c)
584 self.assertFalse(".real" in c, "Shouldn't have completed on a[0]: %s" % c)
577
585
578 def _(line, cursor_pos, expect, message, completion):
586 def _(line, cursor_pos, expect, message, completion):
579 with greedy_completion(), provisionalcompleter():
587 with greedy_completion(), provisionalcompleter():
580 ip.Completer.use_jedi = False
588 ip.Completer.use_jedi = False
581 _, c = ip.complete(".", line=line, cursor_pos=cursor_pos)
589 _, c = ip.complete(".", line=line, cursor_pos=cursor_pos)
582 self.assertIn(expect, c, message % c)
590 self.assertIn(expect, c, message % c)
583
591
584 ip.Completer.use_jedi = True
592 ip.Completer.use_jedi = True
585 with provisionalcompleter():
593 with provisionalcompleter():
586 completions = ip.Completer.completions(line, cursor_pos)
594 completions = ip.Completer.completions(line, cursor_pos)
587 self.assertIn(completion, completions)
595 self.assertIn(completion, completions)
588
596
589 with provisionalcompleter():
597 with provisionalcompleter():
590 _(
598 _(
591 "a[0].",
599 "a[0].",
592 5,
600 5,
593 ".real",
601 ".real",
594 "Should have completed on a[0].: %s",
602 "Should have completed on a[0].: %s",
595 Completion(5, 5, "real"),
603 Completion(5, 5, "real"),
596 )
604 )
597 _(
605 _(
598 "a[0].r",
606 "a[0].r",
599 6,
607 6,
600 ".real",
608 ".real",
601 "Should have completed on a[0].r: %s",
609 "Should have completed on a[0].r: %s",
602 Completion(5, 6, "real"),
610 Completion(5, 6, "real"),
603 )
611 )
604
612
605 _(
613 _(
606 "a[0].from_",
614 "a[0].from_",
607 10,
615 10,
608 ".from_bytes",
616 ".from_bytes",
609 "Should have completed on a[0].from_: %s",
617 "Should have completed on a[0].from_: %s",
610 Completion(5, 10, "from_bytes"),
618 Completion(5, 10, "from_bytes"),
611 )
619 )
612 _(
620 _(
613 "assert str.star",
621 "assert str.star",
614 14,
622 14,
615 "str.startswith",
623 "str.startswith",
616 "Should have completed on `assert str.star`: %s",
624 "Should have completed on `assert str.star`: %s",
617 Completion(11, 14, "startswith"),
625 Completion(11, 14, "startswith"),
618 )
626 )
619 _(
627 _(
620 "d['a b'].str",
628 "d['a b'].str",
621 12,
629 12,
622 ".strip",
630 ".strip",
623 "Should have completed on `d['a b'].str`: %s",
631 "Should have completed on `d['a b'].str`: %s",
624 Completion(9, 12, "strip"),
632 Completion(9, 12, "strip"),
625 )
633 )
626
634
627 def test_omit__names(self):
635 def test_omit__names(self):
628 # also happens to test IPCompleter as a configurable
636 # also happens to test IPCompleter as a configurable
629 ip = get_ipython()
637 ip = get_ipython()
630 ip._hidden_attr = 1
638 ip._hidden_attr = 1
631 ip._x = {}
639 ip._x = {}
632 c = ip.Completer
640 c = ip.Completer
633 ip.ex("ip=get_ipython()")
641 ip.ex("ip=get_ipython()")
634 cfg = Config()
642 cfg = Config()
635 cfg.IPCompleter.omit__names = 0
643 cfg.IPCompleter.omit__names = 0
636 c.update_config(cfg)
644 c.update_config(cfg)
637 with provisionalcompleter():
645 with provisionalcompleter():
638 c.use_jedi = False
646 c.use_jedi = False
639 s, matches = c.complete("ip.")
647 s, matches = c.complete("ip.")
640 self.assertIn("ip.__str__", matches)
648 self.assertIn("ip.__str__", matches)
641 self.assertIn("ip._hidden_attr", matches)
649 self.assertIn("ip._hidden_attr", matches)
642
650
643 # c.use_jedi = True
651 # c.use_jedi = True
644 # completions = set(c.completions('ip.', 3))
652 # completions = set(c.completions('ip.', 3))
645 # self.assertIn(Completion(3, 3, '__str__'), completions)
653 # self.assertIn(Completion(3, 3, '__str__'), completions)
646 # self.assertIn(Completion(3,3, "_hidden_attr"), completions)
654 # self.assertIn(Completion(3,3, "_hidden_attr"), completions)
647
655
648 cfg = Config()
656 cfg = Config()
649 cfg.IPCompleter.omit__names = 1
657 cfg.IPCompleter.omit__names = 1
650 c.update_config(cfg)
658 c.update_config(cfg)
651 with provisionalcompleter():
659 with provisionalcompleter():
652 c.use_jedi = False
660 c.use_jedi = False
653 s, matches = c.complete("ip.")
661 s, matches = c.complete("ip.")
654 self.assertNotIn("ip.__str__", matches)
662 self.assertNotIn("ip.__str__", matches)
655 # self.assertIn('ip._hidden_attr', matches)
663 # self.assertIn('ip._hidden_attr', matches)
656
664
657 # c.use_jedi = True
665 # c.use_jedi = True
658 # completions = set(c.completions('ip.', 3))
666 # completions = set(c.completions('ip.', 3))
659 # self.assertNotIn(Completion(3,3,'__str__'), completions)
667 # self.assertNotIn(Completion(3,3,'__str__'), completions)
660 # self.assertIn(Completion(3,3, "_hidden_attr"), completions)
668 # self.assertIn(Completion(3,3, "_hidden_attr"), completions)
661
669
662 cfg = Config()
670 cfg = Config()
663 cfg.IPCompleter.omit__names = 2
671 cfg.IPCompleter.omit__names = 2
664 c.update_config(cfg)
672 c.update_config(cfg)
665 with provisionalcompleter():
673 with provisionalcompleter():
666 c.use_jedi = False
674 c.use_jedi = False
667 s, matches = c.complete("ip.")
675 s, matches = c.complete("ip.")
668 self.assertNotIn("ip.__str__", matches)
676 self.assertNotIn("ip.__str__", matches)
669 self.assertNotIn("ip._hidden_attr", matches)
677 self.assertNotIn("ip._hidden_attr", matches)
670
678
671 # c.use_jedi = True
679 # c.use_jedi = True
672 # completions = set(c.completions('ip.', 3))
680 # completions = set(c.completions('ip.', 3))
673 # self.assertNotIn(Completion(3,3,'__str__'), completions)
681 # self.assertNotIn(Completion(3,3,'__str__'), completions)
674 # self.assertNotIn(Completion(3,3, "_hidden_attr"), completions)
682 # self.assertNotIn(Completion(3,3, "_hidden_attr"), completions)
675
683
676 with provisionalcompleter():
684 with provisionalcompleter():
677 c.use_jedi = False
685 c.use_jedi = False
678 s, matches = c.complete("ip._x.")
686 s, matches = c.complete("ip._x.")
679 self.assertIn("ip._x.keys", matches)
687 self.assertIn("ip._x.keys", matches)
680
688
681 # c.use_jedi = True
689 # c.use_jedi = True
682 # completions = set(c.completions('ip._x.', 6))
690 # completions = set(c.completions('ip._x.', 6))
683 # self.assertIn(Completion(6,6, "keys"), completions)
691 # self.assertIn(Completion(6,6, "keys"), completions)
684
692
685 del ip._hidden_attr
693 del ip._hidden_attr
686 del ip._x
694 del ip._x
687
695
688 def test_limit_to__all__False_ok(self):
696 def test_limit_to__all__False_ok(self):
689 """
697 """
690 Limit to all is deprecated, once we remove it this test can go away.
698 Limit to all is deprecated, once we remove it this test can go away.
691 """
699 """
692 ip = get_ipython()
700 ip = get_ipython()
693 c = ip.Completer
701 c = ip.Completer
694 c.use_jedi = False
702 c.use_jedi = False
695 ip.ex("class D: x=24")
703 ip.ex("class D: x=24")
696 ip.ex("d=D()")
704 ip.ex("d=D()")
697 cfg = Config()
705 cfg = Config()
698 cfg.IPCompleter.limit_to__all__ = False
706 cfg.IPCompleter.limit_to__all__ = False
699 c.update_config(cfg)
707 c.update_config(cfg)
700 s, matches = c.complete("d.")
708 s, matches = c.complete("d.")
701 self.assertIn("d.x", matches)
709 self.assertIn("d.x", matches)
702
710
703 def test_get__all__entries_ok(self):
711 def test_get__all__entries_ok(self):
704 class A:
712 class A:
705 __all__ = ["x", 1]
713 __all__ = ["x", 1]
706
714
707 words = completer.get__all__entries(A())
715 words = completer.get__all__entries(A())
708 self.assertEqual(words, ["x"])
716 self.assertEqual(words, ["x"])
709
717
710 def test_get__all__entries_no__all__ok(self):
718 def test_get__all__entries_no__all__ok(self):
711 class A:
719 class A:
712 pass
720 pass
713
721
714 words = completer.get__all__entries(A())
722 words = completer.get__all__entries(A())
715 self.assertEqual(words, [])
723 self.assertEqual(words, [])
716
724
717 def test_func_kw_completions(self):
725 def test_func_kw_completions(self):
718 ip = get_ipython()
726 ip = get_ipython()
719 c = ip.Completer
727 c = ip.Completer
720 c.use_jedi = False
728 c.use_jedi = False
721 ip.ex("def myfunc(a=1,b=2): return a+b")
729 ip.ex("def myfunc(a=1,b=2): return a+b")
722 s, matches = c.complete(None, "myfunc(1,b")
730 s, matches = c.complete(None, "myfunc(1,b")
723 self.assertIn("b=", matches)
731 self.assertIn("b=", matches)
724 # Simulate completing with cursor right after b (pos==10):
732 # Simulate completing with cursor right after b (pos==10):
725 s, matches = c.complete(None, "myfunc(1,b)", 10)
733 s, matches = c.complete(None, "myfunc(1,b)", 10)
726 self.assertIn("b=", matches)
734 self.assertIn("b=", matches)
727 s, matches = c.complete(None, 'myfunc(a="escaped\\")string",b')
735 s, matches = c.complete(None, 'myfunc(a="escaped\\")string",b')
728 self.assertIn("b=", matches)
736 self.assertIn("b=", matches)
729 # builtin function
737 # builtin function
730 s, matches = c.complete(None, "min(k, k")
738 s, matches = c.complete(None, "min(k, k")
731 self.assertIn("key=", matches)
739 self.assertIn("key=", matches)
732
740
733 def test_default_arguments_from_docstring(self):
741 def test_default_arguments_from_docstring(self):
734 ip = get_ipython()
742 ip = get_ipython()
735 c = ip.Completer
743 c = ip.Completer
736 kwd = c._default_arguments_from_docstring("min(iterable[, key=func]) -> value")
744 kwd = c._default_arguments_from_docstring("min(iterable[, key=func]) -> value")
737 self.assertEqual(kwd, ["key"])
745 self.assertEqual(kwd, ["key"])
738 # with cython type etc
746 # with cython type etc
739 kwd = c._default_arguments_from_docstring(
747 kwd = c._default_arguments_from_docstring(
740 "Minuit.migrad(self, int ncall=10000, resume=True, int nsplit=1)\n"
748 "Minuit.migrad(self, int ncall=10000, resume=True, int nsplit=1)\n"
741 )
749 )
742 self.assertEqual(kwd, ["ncall", "resume", "nsplit"])
750 self.assertEqual(kwd, ["ncall", "resume", "nsplit"])
743 # white spaces
751 # white spaces
744 kwd = c._default_arguments_from_docstring(
752 kwd = c._default_arguments_from_docstring(
745 "\n Minuit.migrad(self, int ncall=10000, resume=True, int nsplit=1)\n"
753 "\n Minuit.migrad(self, int ncall=10000, resume=True, int nsplit=1)\n"
746 )
754 )
747 self.assertEqual(kwd, ["ncall", "resume", "nsplit"])
755 self.assertEqual(kwd, ["ncall", "resume", "nsplit"])
748
756
749 def test_line_magics(self):
757 def test_line_magics(self):
750 ip = get_ipython()
758 ip = get_ipython()
751 c = ip.Completer
759 c = ip.Completer
752 s, matches = c.complete(None, "lsmag")
760 s, matches = c.complete(None, "lsmag")
753 self.assertIn("%lsmagic", matches)
761 self.assertIn("%lsmagic", matches)
754 s, matches = c.complete(None, "%lsmag")
762 s, matches = c.complete(None, "%lsmag")
755 self.assertIn("%lsmagic", matches)
763 self.assertIn("%lsmagic", matches)
756
764
757 def test_cell_magics(self):
765 def test_cell_magics(self):
758 from IPython.core.magic import register_cell_magic
766 from IPython.core.magic import register_cell_magic
759
767
760 @register_cell_magic
768 @register_cell_magic
761 def _foo_cellm(line, cell):
769 def _foo_cellm(line, cell):
762 pass
770 pass
763
771
764 ip = get_ipython()
772 ip = get_ipython()
765 c = ip.Completer
773 c = ip.Completer
766
774
767 s, matches = c.complete(None, "_foo_ce")
775 s, matches = c.complete(None, "_foo_ce")
768 self.assertIn("%%_foo_cellm", matches)
776 self.assertIn("%%_foo_cellm", matches)
769 s, matches = c.complete(None, "%%_foo_ce")
777 s, matches = c.complete(None, "%%_foo_ce")
770 self.assertIn("%%_foo_cellm", matches)
778 self.assertIn("%%_foo_cellm", matches)
771
779
772 def test_line_cell_magics(self):
780 def test_line_cell_magics(self):
773 from IPython.core.magic import register_line_cell_magic
781 from IPython.core.magic import register_line_cell_magic
774
782
775 @register_line_cell_magic
783 @register_line_cell_magic
776 def _bar_cellm(line, cell):
784 def _bar_cellm(line, cell):
777 pass
785 pass
778
786
779 ip = get_ipython()
787 ip = get_ipython()
780 c = ip.Completer
788 c = ip.Completer
781
789
782 # The policy here is trickier, see comments in completion code. The
790 # The policy here is trickier, see comments in completion code. The
783 # returned values depend on whether the user passes %% or not explicitly,
791 # 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
792 # and this will show a difference if the same name is both a line and cell
785 # magic.
793 # magic.
786 s, matches = c.complete(None, "_bar_ce")
794 s, matches = c.complete(None, "_bar_ce")
787 self.assertIn("%_bar_cellm", matches)
795 self.assertIn("%_bar_cellm", matches)
788 self.assertIn("%%_bar_cellm", matches)
796 self.assertIn("%%_bar_cellm", matches)
789 s, matches = c.complete(None, "%_bar_ce")
797 s, matches = c.complete(None, "%_bar_ce")
790 self.assertIn("%_bar_cellm", matches)
798 self.assertIn("%_bar_cellm", matches)
791 self.assertIn("%%_bar_cellm", matches)
799 self.assertIn("%%_bar_cellm", matches)
792 s, matches = c.complete(None, "%%_bar_ce")
800 s, matches = c.complete(None, "%%_bar_ce")
793 self.assertNotIn("%_bar_cellm", matches)
801 self.assertNotIn("%_bar_cellm", matches)
794 self.assertIn("%%_bar_cellm", matches)
802 self.assertIn("%%_bar_cellm", matches)
795
803
796 def test_magic_completion_order(self):
804 def test_magic_completion_order(self):
797 ip = get_ipython()
805 ip = get_ipython()
798 c = ip.Completer
806 c = ip.Completer
799
807
800 # Test ordering of line and cell magics.
808 # Test ordering of line and cell magics.
801 text, matches = c.complete("timeit")
809 text, matches = c.complete("timeit")
802 self.assertEqual(matches, ["%timeit", "%%timeit"])
810 self.assertEqual(matches, ["%timeit", "%%timeit"])
803
811
804 def test_magic_completion_shadowing(self):
812 def test_magic_completion_shadowing(self):
805 ip = get_ipython()
813 ip = get_ipython()
806 c = ip.Completer
814 c = ip.Completer
807 c.use_jedi = False
815 c.use_jedi = False
808
816
809 # Before importing matplotlib, %matplotlib magic should be the only option.
817 # Before importing matplotlib, %matplotlib magic should be the only option.
810 text, matches = c.complete("mat")
818 text, matches = c.complete("mat")
811 self.assertEqual(matches, ["%matplotlib"])
819 self.assertEqual(matches, ["%matplotlib"])
812
820
813 # The newly introduced name should shadow the magic.
821 # The newly introduced name should shadow the magic.
814 ip.run_cell("matplotlib = 1")
822 ip.run_cell("matplotlib = 1")
815 text, matches = c.complete("mat")
823 text, matches = c.complete("mat")
816 self.assertEqual(matches, ["matplotlib"])
824 self.assertEqual(matches, ["matplotlib"])
817
825
818 # After removing matplotlib from namespace, the magic should again be
826 # After removing matplotlib from namespace, the magic should again be
819 # the only option.
827 # the only option.
820 del ip.user_ns["matplotlib"]
828 del ip.user_ns["matplotlib"]
821 text, matches = c.complete("mat")
829 text, matches = c.complete("mat")
822 self.assertEqual(matches, ["%matplotlib"])
830 self.assertEqual(matches, ["%matplotlib"])
823
831
824 def test_magic_completion_shadowing_explicit(self):
832 def test_magic_completion_shadowing_explicit(self):
825 """
833 """
826 If the user try to complete a shadowed magic, and explicit % start should
834 If the user try to complete a shadowed magic, and explicit % start should
827 still return the completions.
835 still return the completions.
828 """
836 """
829 ip = get_ipython()
837 ip = get_ipython()
830 c = ip.Completer
838 c = ip.Completer
831
839
832 # Before importing matplotlib, %matplotlib magic should be the only option.
840 # Before importing matplotlib, %matplotlib magic should be the only option.
833 text, matches = c.complete("%mat")
841 text, matches = c.complete("%mat")
834 self.assertEqual(matches, ["%matplotlib"])
842 self.assertEqual(matches, ["%matplotlib"])
835
843
836 ip.run_cell("matplotlib = 1")
844 ip.run_cell("matplotlib = 1")
837
845
838 # After removing matplotlib from namespace, the magic should still be
846 # After removing matplotlib from namespace, the magic should still be
839 # the only option.
847 # the only option.
840 text, matches = c.complete("%mat")
848 text, matches = c.complete("%mat")
841 self.assertEqual(matches, ["%matplotlib"])
849 self.assertEqual(matches, ["%matplotlib"])
842
850
843 def test_magic_config(self):
851 def test_magic_config(self):
844 ip = get_ipython()
852 ip = get_ipython()
845 c = ip.Completer
853 c = ip.Completer
846
854
847 s, matches = c.complete(None, "conf")
855 s, matches = c.complete(None, "conf")
848 self.assertIn("%config", matches)
856 self.assertIn("%config", matches)
849 s, matches = c.complete(None, "conf")
857 s, matches = c.complete(None, "conf")
850 self.assertNotIn("AliasManager", matches)
858 self.assertNotIn("AliasManager", matches)
851 s, matches = c.complete(None, "config ")
859 s, matches = c.complete(None, "config ")
852 self.assertIn("AliasManager", matches)
860 self.assertIn("AliasManager", matches)
853 s, matches = c.complete(None, "%config ")
861 s, matches = c.complete(None, "%config ")
854 self.assertIn("AliasManager", matches)
862 self.assertIn("AliasManager", matches)
855 s, matches = c.complete(None, "config Ali")
863 s, matches = c.complete(None, "config Ali")
856 self.assertListEqual(["AliasManager"], matches)
864 self.assertListEqual(["AliasManager"], matches)
857 s, matches = c.complete(None, "%config Ali")
865 s, matches = c.complete(None, "%config Ali")
858 self.assertListEqual(["AliasManager"], matches)
866 self.assertListEqual(["AliasManager"], matches)
859 s, matches = c.complete(None, "config AliasManager")
867 s, matches = c.complete(None, "config AliasManager")
860 self.assertListEqual(["AliasManager"], matches)
868 self.assertListEqual(["AliasManager"], matches)
861 s, matches = c.complete(None, "%config AliasManager")
869 s, matches = c.complete(None, "%config AliasManager")
862 self.assertListEqual(["AliasManager"], matches)
870 self.assertListEqual(["AliasManager"], matches)
863 s, matches = c.complete(None, "config AliasManager.")
871 s, matches = c.complete(None, "config AliasManager.")
864 self.assertIn("AliasManager.default_aliases", matches)
872 self.assertIn("AliasManager.default_aliases", matches)
865 s, matches = c.complete(None, "%config AliasManager.")
873 s, matches = c.complete(None, "%config AliasManager.")
866 self.assertIn("AliasManager.default_aliases", matches)
874 self.assertIn("AliasManager.default_aliases", matches)
867 s, matches = c.complete(None, "config AliasManager.de")
875 s, matches = c.complete(None, "config AliasManager.de")
868 self.assertListEqual(["AliasManager.default_aliases"], matches)
876 self.assertListEqual(["AliasManager.default_aliases"], matches)
869 s, matches = c.complete(None, "config AliasManager.de")
877 s, matches = c.complete(None, "config AliasManager.de")
870 self.assertListEqual(["AliasManager.default_aliases"], matches)
878 self.assertListEqual(["AliasManager.default_aliases"], matches)
871
879
872 def test_magic_color(self):
880 def test_magic_color(self):
873 ip = get_ipython()
881 ip = get_ipython()
874 c = ip.Completer
882 c = ip.Completer
875
883
876 s, matches = c.complete(None, "colo")
884 s, matches = c.complete(None, "colo")
877 self.assertIn("%colors", matches)
885 self.assertIn("%colors", matches)
878 s, matches = c.complete(None, "colo")
886 s, matches = c.complete(None, "colo")
879 self.assertNotIn("NoColor", matches)
887 self.assertNotIn("NoColor", matches)
880 s, matches = c.complete(None, "%colors") # No trailing space
888 s, matches = c.complete(None, "%colors") # No trailing space
881 self.assertNotIn("NoColor", matches)
889 self.assertNotIn("NoColor", matches)
882 s, matches = c.complete(None, "colors ")
890 s, matches = c.complete(None, "colors ")
883 self.assertIn("NoColor", matches)
891 self.assertIn("NoColor", matches)
884 s, matches = c.complete(None, "%colors ")
892 s, matches = c.complete(None, "%colors ")
885 self.assertIn("NoColor", matches)
893 self.assertIn("NoColor", matches)
886 s, matches = c.complete(None, "colors NoCo")
894 s, matches = c.complete(None, "colors NoCo")
887 self.assertListEqual(["NoColor"], matches)
895 self.assertListEqual(["NoColor"], matches)
888 s, matches = c.complete(None, "%colors NoCo")
896 s, matches = c.complete(None, "%colors NoCo")
889 self.assertListEqual(["NoColor"], matches)
897 self.assertListEqual(["NoColor"], matches)
890
898
891 def test_match_dict_keys(self):
899 def test_match_dict_keys(self):
892 """
900 """
893 Test that match_dict_keys works on a couple of use case does return what
901 Test that match_dict_keys works on a couple of use case does return what
894 expected, and does not crash
902 expected, and does not crash
895 """
903 """
896 delims = " \t\n`!@#$^&*()=+[{]}\\|;:'\",<>?"
904 delims = " \t\n`!@#$^&*()=+[{]}\\|;:'\",<>?"
897
905
898 def match(*args, **kwargs):
906 def match(*args, **kwargs):
899 quote, offset, matches = match_dict_keys(*args, delims=delims, **kwargs)
907 quote, offset, matches = match_dict_keys(*args, delims=delims, **kwargs)
900 return quote, offset, list(matches)
908 return quote, offset, list(matches)
901
909
902 keys = ["foo", b"far"]
910 keys = ["foo", b"far"]
903 assert match(keys, "b'") == ("'", 2, ["far"])
911 assert match(keys, "b'") == ("'", 2, ["far"])
904 assert match(keys, "b'f") == ("'", 2, ["far"])
912 assert match(keys, "b'f") == ("'", 2, ["far"])
905 assert match(keys, 'b"') == ('"', 2, ["far"])
913 assert match(keys, 'b"') == ('"', 2, ["far"])
906 assert match(keys, 'b"f') == ('"', 2, ["far"])
914 assert match(keys, 'b"f') == ('"', 2, ["far"])
907
915
908 assert match(keys, "'") == ("'", 1, ["foo"])
916 assert match(keys, "'") == ("'", 1, ["foo"])
909 assert match(keys, "'f") == ("'", 1, ["foo"])
917 assert match(keys, "'f") == ("'", 1, ["foo"])
910 assert match(keys, '"') == ('"', 1, ["foo"])
918 assert match(keys, '"') == ('"', 1, ["foo"])
911 assert match(keys, '"f') == ('"', 1, ["foo"])
919 assert match(keys, '"f') == ('"', 1, ["foo"])
912
920
913 # Completion on first item of tuple
921 # Completion on first item of tuple
914 keys = [("foo", 1111), ("foo", 2222), (3333, "bar"), (3333, "test")]
922 keys = [("foo", 1111), ("foo", 2222), (3333, "bar"), (3333, "test")]
915 assert match(keys, "'f") == ("'", 1, ["foo"])
923 assert match(keys, "'f") == ("'", 1, ["foo"])
916 assert match(keys, "33") == ("", 0, ["3333"])
924 assert match(keys, "33") == ("", 0, ["3333"])
917
925
918 # Completion on numbers
926 # Completion on numbers
919 keys = [
927 keys = [
920 0xDEADBEEF,
928 0xDEADBEEF,
921 1111,
929 1111,
922 1234,
930 1234,
923 "1999",
931 "1999",
924 0b10101,
932 0b10101,
925 22,
933 22,
926 ] # 0xDEADBEEF = 3735928559; 0b10101 = 21
934 ] # 0xDEADBEEF = 3735928559; 0b10101 = 21
927 assert match(keys, "0xdead") == ("", 0, ["0xdeadbeef"])
935 assert match(keys, "0xdead") == ("", 0, ["0xdeadbeef"])
928 assert match(keys, "1") == ("", 0, ["1111", "1234"])
936 assert match(keys, "1") == ("", 0, ["1111", "1234"])
929 assert match(keys, "2") == ("", 0, ["21", "22"])
937 assert match(keys, "2") == ("", 0, ["21", "22"])
930 assert match(keys, "0b101") == ("", 0, ["0b10101", "0b10110"])
938 assert match(keys, "0b101") == ("", 0, ["0b10101", "0b10110"])
931
939
932 # Should yield on variables
940 # Should yield on variables
933 assert match(keys, "a_variable") == ("", 0, [])
941 assert match(keys, "a_variable") == ("", 0, [])
934
942
935 # Should pass over invalid literals
943 # Should pass over invalid literals
936 assert match(keys, "'' ''") == ("", 0, [])
944 assert match(keys, "'' ''") == ("", 0, [])
937
945
938 def test_match_dict_keys_tuple(self):
946 def test_match_dict_keys_tuple(self):
939 """
947 """
940 Test that match_dict_keys called with extra prefix works on a couple of use case,
948 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.
949 does return what expected, and does not crash.
942 """
950 """
943 delims = " \t\n`!@#$^&*()=+[{]}\\|;:'\",<>?"
951 delims = " \t\n`!@#$^&*()=+[{]}\\|;:'\",<>?"
944
952
945 keys = [("foo", "bar"), ("foo", "oof"), ("foo", b"bar"), ('other', 'test')]
953 keys = [("foo", "bar"), ("foo", "oof"), ("foo", b"bar"), ('other', 'test')]
946
954
947 def match(*args, extra=None, **kwargs):
955 def match(*args, extra=None, **kwargs):
948 quote, offset, matches = match_dict_keys(
956 quote, offset, matches = match_dict_keys(
949 *args, delims=delims, extra_prefix=extra, **kwargs
957 *args, delims=delims, extra_prefix=extra, **kwargs
950 )
958 )
951 return quote, offset, list(matches)
959 return quote, offset, list(matches)
952
960
953 # Completion on first key == "foo"
961 # Completion on first key == "foo"
954 assert match(keys, "'", extra=("foo",)) == ("'", 1, ["bar", "oof"])
962 assert match(keys, "'", extra=("foo",)) == ("'", 1, ["bar", "oof"])
955 assert match(keys, '"', extra=("foo",)) == ('"', 1, ["bar", "oof"])
963 assert match(keys, '"', extra=("foo",)) == ('"', 1, ["bar", "oof"])
956 assert match(keys, "'o", extra=("foo",)) == ("'", 1, ["oof"])
964 assert match(keys, "'o", extra=("foo",)) == ("'", 1, ["oof"])
957 assert match(keys, '"o', extra=("foo",)) == ('"', 1, ["oof"])
965 assert match(keys, '"o', extra=("foo",)) == ('"', 1, ["oof"])
958 assert match(keys, "b'", extra=("foo",)) == ("'", 2, ["bar"])
966 assert match(keys, "b'", extra=("foo",)) == ("'", 2, ["bar"])
959 assert match(keys, 'b"', extra=("foo",)) == ('"', 2, ["bar"])
967 assert match(keys, 'b"', extra=("foo",)) == ('"', 2, ["bar"])
960 assert match(keys, "b'b", extra=("foo",)) == ("'", 2, ["bar"])
968 assert match(keys, "b'b", extra=("foo",)) == ("'", 2, ["bar"])
961 assert match(keys, 'b"b', extra=("foo",)) == ('"', 2, ["bar"])
969 assert match(keys, 'b"b', extra=("foo",)) == ('"', 2, ["bar"])
962
970
963 # No Completion
971 # No Completion
964 assert match(keys, "'", extra=("no_foo",)) == ("'", 1, [])
972 assert match(keys, "'", extra=("no_foo",)) == ("'", 1, [])
965 assert match(keys, "'", extra=("fo",)) == ("'", 1, [])
973 assert match(keys, "'", extra=("fo",)) == ("'", 1, [])
966
974
967 keys = [("foo1", "foo2", "foo3", "foo4"), ("foo1", "foo2", "bar", "foo4")]
975 keys = [("foo1", "foo2", "foo3", "foo4"), ("foo1", "foo2", "bar", "foo4")]
968 assert match(keys, "'foo", extra=("foo1",)) == ("'", 1, ["foo2"])
976 assert match(keys, "'foo", extra=("foo1",)) == ("'", 1, ["foo2"])
969 assert match(keys, "'foo", extra=("foo1", "foo2")) == ("'", 1, ["foo3"])
977 assert match(keys, "'foo", extra=("foo1", "foo2")) == ("'", 1, ["foo3"])
970 assert match(keys, "'foo", extra=("foo1", "foo2", "foo3")) == ("'", 1, ["foo4"])
978 assert match(keys, "'foo", extra=("foo1", "foo2", "foo3")) == ("'", 1, ["foo4"])
971 assert match(keys, "'foo", extra=("foo1", "foo2", "foo3", "foo4")) == (
979 assert match(keys, "'foo", extra=("foo1", "foo2", "foo3", "foo4")) == (
972 "'",
980 "'",
973 1,
981 1,
974 [],
982 [],
975 )
983 )
976
984
977 keys = [("foo", 1111), ("foo", "2222"), (3333, "bar"), (3333, 4444)]
985 keys = [("foo", 1111), ("foo", "2222"), (3333, "bar"), (3333, 4444)]
978 assert match(keys, "'", extra=("foo",)) == ("'", 1, ["2222"])
986 assert match(keys, "'", extra=("foo",)) == ("'", 1, ["2222"])
979 assert match(keys, "", extra=("foo",)) == ("", 0, ["1111", "'2222'"])
987 assert match(keys, "", extra=("foo",)) == ("", 0, ["1111", "'2222'"])
980 assert match(keys, "'", extra=(3333,)) == ("'", 1, ["bar"])
988 assert match(keys, "'", extra=(3333,)) == ("'", 1, ["bar"])
981 assert match(keys, "", extra=(3333,)) == ("", 0, ["'bar'", "4444"])
989 assert match(keys, "", extra=(3333,)) == ("", 0, ["'bar'", "4444"])
982 assert match(keys, "'", extra=("3333",)) == ("'", 1, [])
990 assert match(keys, "'", extra=("3333",)) == ("'", 1, [])
983 assert match(keys, "33") == ("", 0, ["3333"])
991 assert match(keys, "33") == ("", 0, ["3333"])
984
992
985 def test_dict_key_completion_closures(self):
993 def test_dict_key_completion_closures(self):
986 ip = get_ipython()
994 ip = get_ipython()
987 complete = ip.Completer.complete
995 complete = ip.Completer.complete
988 ip.Completer.auto_close_dict_keys = True
996 ip.Completer.auto_close_dict_keys = True
989
997
990 ip.user_ns["d"] = {
998 ip.user_ns["d"] = {
991 # tuple only
999 # tuple only
992 ("aa", 11): None,
1000 ("aa", 11): None,
993 # tuple and non-tuple
1001 # tuple and non-tuple
994 ("bb", 22): None,
1002 ("bb", 22): None,
995 "bb": None,
1003 "bb": None,
996 # non-tuple only
1004 # non-tuple only
997 "cc": None,
1005 "cc": None,
998 # numeric tuple only
1006 # numeric tuple only
999 (77, "x"): None,
1007 (77, "x"): None,
1000 # numeric tuple and non-tuple
1008 # numeric tuple and non-tuple
1001 (88, "y"): None,
1009 (88, "y"): None,
1002 88: None,
1010 88: None,
1003 # numeric non-tuple only
1011 # numeric non-tuple only
1004 99: None,
1012 99: None,
1005 }
1013 }
1006
1014
1007 _, matches = complete(line_buffer="d[")
1015 _, matches = complete(line_buffer="d[")
1008 # should append `, ` if matches a tuple only
1016 # should append `, ` if matches a tuple only
1009 self.assertIn("'aa', ", matches)
1017 self.assertIn("'aa', ", matches)
1010 # should not append anything if matches a tuple and an item
1018 # should not append anything if matches a tuple and an item
1011 self.assertIn("'bb'", matches)
1019 self.assertIn("'bb'", matches)
1012 # should append `]` if matches and item only
1020 # should append `]` if matches and item only
1013 self.assertIn("'cc']", matches)
1021 self.assertIn("'cc']", matches)
1014
1022
1015 # should append `, ` if matches a tuple only
1023 # should append `, ` if matches a tuple only
1016 self.assertIn("77, ", matches)
1024 self.assertIn("77, ", matches)
1017 # should not append anything if matches a tuple and an item
1025 # should not append anything if matches a tuple and an item
1018 self.assertIn("88", matches)
1026 self.assertIn("88", matches)
1019 # should append `]` if matches and item only
1027 # should append `]` if matches and item only
1020 self.assertIn("99]", matches)
1028 self.assertIn("99]", matches)
1021
1029
1022 _, matches = complete(line_buffer="d['aa', ")
1030 _, matches = complete(line_buffer="d['aa', ")
1023 # should restrict matches to those matching tuple prefix
1031 # should restrict matches to those matching tuple prefix
1024 self.assertIn("11]", matches)
1032 self.assertIn("11]", matches)
1025 self.assertNotIn("'bb'", matches)
1033 self.assertNotIn("'bb'", matches)
1026 self.assertNotIn("'bb', ", matches)
1034 self.assertNotIn("'bb', ", matches)
1027 self.assertNotIn("'bb']", matches)
1035 self.assertNotIn("'bb']", matches)
1028 self.assertNotIn("'cc'", matches)
1036 self.assertNotIn("'cc'", matches)
1029 self.assertNotIn("'cc', ", matches)
1037 self.assertNotIn("'cc', ", matches)
1030 self.assertNotIn("'cc']", matches)
1038 self.assertNotIn("'cc']", matches)
1031 ip.Completer.auto_close_dict_keys = False
1039 ip.Completer.auto_close_dict_keys = False
1032
1040
1033 def test_dict_key_completion_string(self):
1041 def test_dict_key_completion_string(self):
1034 """Test dictionary key completion for string keys"""
1042 """Test dictionary key completion for string keys"""
1035 ip = get_ipython()
1043 ip = get_ipython()
1036 complete = ip.Completer.complete
1044 complete = ip.Completer.complete
1037
1045
1038 ip.user_ns["d"] = {"abc": None}
1046 ip.user_ns["d"] = {"abc": None}
1039
1047
1040 # check completion at different stages
1048 # check completion at different stages
1041 _, matches = complete(line_buffer="d[")
1049 _, matches = complete(line_buffer="d[")
1042 self.assertIn("'abc'", matches)
1050 self.assertIn("'abc'", matches)
1043 self.assertNotIn("'abc']", matches)
1051 self.assertNotIn("'abc']", matches)
1044
1052
1045 _, matches = complete(line_buffer="d['")
1053 _, matches = complete(line_buffer="d['")
1046 self.assertIn("abc", matches)
1054 self.assertIn("abc", matches)
1047 self.assertNotIn("abc']", matches)
1055 self.assertNotIn("abc']", matches)
1048
1056
1049 _, matches = complete(line_buffer="d['a")
1057 _, matches = complete(line_buffer="d['a")
1050 self.assertIn("abc", matches)
1058 self.assertIn("abc", matches)
1051 self.assertNotIn("abc']", matches)
1059 self.assertNotIn("abc']", matches)
1052
1060
1053 # check use of different quoting
1061 # check use of different quoting
1054 _, matches = complete(line_buffer='d["')
1062 _, matches = complete(line_buffer='d["')
1055 self.assertIn("abc", matches)
1063 self.assertIn("abc", matches)
1056 self.assertNotIn('abc"]', matches)
1064 self.assertNotIn('abc"]', matches)
1057
1065
1058 _, matches = complete(line_buffer='d["a')
1066 _, matches = complete(line_buffer='d["a')
1059 self.assertIn("abc", matches)
1067 self.assertIn("abc", matches)
1060 self.assertNotIn('abc"]', matches)
1068 self.assertNotIn('abc"]', matches)
1061
1069
1062 # check sensitivity to following context
1070 # check sensitivity to following context
1063 _, matches = complete(line_buffer="d[]", cursor_pos=2)
1071 _, matches = complete(line_buffer="d[]", cursor_pos=2)
1064 self.assertIn("'abc'", matches)
1072 self.assertIn("'abc'", matches)
1065
1073
1066 _, matches = complete(line_buffer="d['']", cursor_pos=3)
1074 _, matches = complete(line_buffer="d['']", cursor_pos=3)
1067 self.assertIn("abc", matches)
1075 self.assertIn("abc", matches)
1068 self.assertNotIn("abc'", matches)
1076 self.assertNotIn("abc'", matches)
1069 self.assertNotIn("abc']", matches)
1077 self.assertNotIn("abc']", matches)
1070
1078
1071 # check multiple solutions are correctly returned and that noise is not
1079 # check multiple solutions are correctly returned and that noise is not
1072 ip.user_ns["d"] = {
1080 ip.user_ns["d"] = {
1073 "abc": None,
1081 "abc": None,
1074 "abd": None,
1082 "abd": None,
1075 "bad": None,
1083 "bad": None,
1076 object(): None,
1084 object(): None,
1077 5: None,
1085 5: None,
1078 ("abe", None): None,
1086 ("abe", None): None,
1079 (None, "abf"): None
1087 (None, "abf"): None
1080 }
1088 }
1081
1089
1082 _, matches = complete(line_buffer="d['a")
1090 _, matches = complete(line_buffer="d['a")
1083 self.assertIn("abc", matches)
1091 self.assertIn("abc", matches)
1084 self.assertIn("abd", matches)
1092 self.assertIn("abd", matches)
1085 self.assertNotIn("bad", matches)
1093 self.assertNotIn("bad", matches)
1086 self.assertNotIn("abe", matches)
1094 self.assertNotIn("abe", matches)
1087 self.assertNotIn("abf", matches)
1095 self.assertNotIn("abf", matches)
1088 assert not any(m.endswith(("]", '"', "'")) for m in matches), matches
1096 assert not any(m.endswith(("]", '"', "'")) for m in matches), matches
1089
1097
1090 # check escaping and whitespace
1098 # check escaping and whitespace
1091 ip.user_ns["d"] = {"a\nb": None, "a'b": None, 'a"b': None, "a word": None}
1099 ip.user_ns["d"] = {"a\nb": None, "a'b": None, 'a"b': None, "a word": None}
1092 _, matches = complete(line_buffer="d['a")
1100 _, matches = complete(line_buffer="d['a")
1093 self.assertIn("a\\nb", matches)
1101 self.assertIn("a\\nb", matches)
1094 self.assertIn("a\\'b", matches)
1102 self.assertIn("a\\'b", matches)
1095 self.assertIn('a"b', matches)
1103 self.assertIn('a"b', matches)
1096 self.assertIn("a word", matches)
1104 self.assertIn("a word", matches)
1097 assert not any(m.endswith(("]", '"', "'")) for m in matches), matches
1105 assert not any(m.endswith(("]", '"', "'")) for m in matches), matches
1098
1106
1099 # - can complete on non-initial word of the string
1107 # - can complete on non-initial word of the string
1100 _, matches = complete(line_buffer="d['a w")
1108 _, matches = complete(line_buffer="d['a w")
1101 self.assertIn("word", matches)
1109 self.assertIn("word", matches)
1102
1110
1103 # - understands quote escaping
1111 # - understands quote escaping
1104 _, matches = complete(line_buffer="d['a\\'")
1112 _, matches = complete(line_buffer="d['a\\'")
1105 self.assertIn("b", matches)
1113 self.assertIn("b", matches)
1106
1114
1107 # - default quoting should work like repr
1115 # - default quoting should work like repr
1108 _, matches = complete(line_buffer="d[")
1116 _, matches = complete(line_buffer="d[")
1109 self.assertIn('"a\'b"', matches)
1117 self.assertIn('"a\'b"', matches)
1110
1118
1111 # - when opening quote with ", possible to match with unescaped apostrophe
1119 # - when opening quote with ", possible to match with unescaped apostrophe
1112 _, matches = complete(line_buffer="d[\"a'")
1120 _, matches = complete(line_buffer="d[\"a'")
1113 self.assertIn("b", matches)
1121 self.assertIn("b", matches)
1114
1122
1115 # need to not split at delims that readline won't split at
1123 # need to not split at delims that readline won't split at
1116 if "-" not in ip.Completer.splitter.delims:
1124 if "-" not in ip.Completer.splitter.delims:
1117 ip.user_ns["d"] = {"before-after": None}
1125 ip.user_ns["d"] = {"before-after": None}
1118 _, matches = complete(line_buffer="d['before-af")
1126 _, matches = complete(line_buffer="d['before-af")
1119 self.assertIn("before-after", matches)
1127 self.assertIn("before-after", matches)
1120
1128
1121 # check completion on tuple-of-string keys at different stage - on first key
1129 # check completion on tuple-of-string keys at different stage - on first key
1122 ip.user_ns["d"] = {('foo', 'bar'): None}
1130 ip.user_ns["d"] = {('foo', 'bar'): None}
1123 _, matches = complete(line_buffer="d[")
1131 _, matches = complete(line_buffer="d[")
1124 self.assertIn("'foo'", matches)
1132 self.assertIn("'foo'", matches)
1125 self.assertNotIn("'foo']", matches)
1133 self.assertNotIn("'foo']", matches)
1126 self.assertNotIn("'bar'", matches)
1134 self.assertNotIn("'bar'", matches)
1127 self.assertNotIn("foo", matches)
1135 self.assertNotIn("foo", matches)
1128 self.assertNotIn("bar", matches)
1136 self.assertNotIn("bar", matches)
1129
1137
1130 # - match the prefix
1138 # - match the prefix
1131 _, matches = complete(line_buffer="d['f")
1139 _, matches = complete(line_buffer="d['f")
1132 self.assertIn("foo", matches)
1140 self.assertIn("foo", matches)
1133 self.assertNotIn("foo']", matches)
1141 self.assertNotIn("foo']", matches)
1134 self.assertNotIn('foo"]', matches)
1142 self.assertNotIn('foo"]', matches)
1135 _, matches = complete(line_buffer="d['foo")
1143 _, matches = complete(line_buffer="d['foo")
1136 self.assertIn("foo", matches)
1144 self.assertIn("foo", matches)
1137
1145
1138 # - can complete on second key
1146 # - can complete on second key
1139 _, matches = complete(line_buffer="d['foo', ")
1147 _, matches = complete(line_buffer="d['foo', ")
1140 self.assertIn("'bar'", matches)
1148 self.assertIn("'bar'", matches)
1141 _, matches = complete(line_buffer="d['foo', 'b")
1149 _, matches = complete(line_buffer="d['foo', 'b")
1142 self.assertIn("bar", matches)
1150 self.assertIn("bar", matches)
1143 self.assertNotIn("foo", matches)
1151 self.assertNotIn("foo", matches)
1144
1152
1145 # - does not propose missing keys
1153 # - does not propose missing keys
1146 _, matches = complete(line_buffer="d['foo', 'f")
1154 _, matches = complete(line_buffer="d['foo', 'f")
1147 self.assertNotIn("bar", matches)
1155 self.assertNotIn("bar", matches)
1148 self.assertNotIn("foo", matches)
1156 self.assertNotIn("foo", matches)
1149
1157
1150 # check sensitivity to following context
1158 # check sensitivity to following context
1151 _, matches = complete(line_buffer="d['foo',]", cursor_pos=8)
1159 _, matches = complete(line_buffer="d['foo',]", cursor_pos=8)
1152 self.assertIn("'bar'", matches)
1160 self.assertIn("'bar'", matches)
1153 self.assertNotIn("bar", matches)
1161 self.assertNotIn("bar", matches)
1154 self.assertNotIn("'foo'", matches)
1162 self.assertNotIn("'foo'", matches)
1155 self.assertNotIn("foo", matches)
1163 self.assertNotIn("foo", matches)
1156
1164
1157 _, matches = complete(line_buffer="d['']", cursor_pos=3)
1165 _, matches = complete(line_buffer="d['']", cursor_pos=3)
1158 self.assertIn("foo", matches)
1166 self.assertIn("foo", matches)
1159 assert not any(m.endswith(("]", '"', "'")) for m in matches), matches
1167 assert not any(m.endswith(("]", '"', "'")) for m in matches), matches
1160
1168
1161 _, matches = complete(line_buffer='d[""]', cursor_pos=3)
1169 _, matches = complete(line_buffer='d[""]', cursor_pos=3)
1162 self.assertIn("foo", matches)
1170 self.assertIn("foo", matches)
1163 assert not any(m.endswith(("]", '"', "'")) for m in matches), matches
1171 assert not any(m.endswith(("]", '"', "'")) for m in matches), matches
1164
1172
1165 _, matches = complete(line_buffer='d["foo","]', cursor_pos=9)
1173 _, matches = complete(line_buffer='d["foo","]', cursor_pos=9)
1166 self.assertIn("bar", matches)
1174 self.assertIn("bar", matches)
1167 assert not any(m.endswith(("]", '"', "'")) for m in matches), matches
1175 assert not any(m.endswith(("]", '"', "'")) for m in matches), matches
1168
1176
1169 _, matches = complete(line_buffer='d["foo",]', cursor_pos=8)
1177 _, matches = complete(line_buffer='d["foo",]', cursor_pos=8)
1170 self.assertIn("'bar'", matches)
1178 self.assertIn("'bar'", matches)
1171 self.assertNotIn("bar", matches)
1179 self.assertNotIn("bar", matches)
1172
1180
1173 # Can complete with longer tuple keys
1181 # Can complete with longer tuple keys
1174 ip.user_ns["d"] = {('foo', 'bar', 'foobar'): None}
1182 ip.user_ns["d"] = {('foo', 'bar', 'foobar'): None}
1175
1183
1176 # - can complete second key
1184 # - can complete second key
1177 _, matches = complete(line_buffer="d['foo', 'b")
1185 _, matches = complete(line_buffer="d['foo', 'b")
1178 self.assertIn("bar", matches)
1186 self.assertIn("bar", matches)
1179 self.assertNotIn("foo", matches)
1187 self.assertNotIn("foo", matches)
1180 self.assertNotIn("foobar", matches)
1188 self.assertNotIn("foobar", matches)
1181
1189
1182 # - can complete third key
1190 # - can complete third key
1183 _, matches = complete(line_buffer="d['foo', 'bar', 'fo")
1191 _, matches = complete(line_buffer="d['foo', 'bar', 'fo")
1184 self.assertIn("foobar", matches)
1192 self.assertIn("foobar", matches)
1185 self.assertNotIn("foo", matches)
1193 self.assertNotIn("foo", matches)
1186 self.assertNotIn("bar", matches)
1194 self.assertNotIn("bar", matches)
1187
1195
1188 def test_dict_key_completion_numbers(self):
1196 def test_dict_key_completion_numbers(self):
1189 ip = get_ipython()
1197 ip = get_ipython()
1190 complete = ip.Completer.complete
1198 complete = ip.Completer.complete
1191
1199
1192 ip.user_ns["d"] = {
1200 ip.user_ns["d"] = {
1193 0xDEADBEEF: None, # 3735928559
1201 0xDEADBEEF: None, # 3735928559
1194 1111: None,
1202 1111: None,
1195 1234: None,
1203 1234: None,
1196 "1999": None,
1204 "1999": None,
1197 0b10101: None, # 21
1205 0b10101: None, # 21
1198 22: None,
1206 22: None,
1199 }
1207 }
1200 _, matches = complete(line_buffer="d[1")
1208 _, matches = complete(line_buffer="d[1")
1201 self.assertIn("1111", matches)
1209 self.assertIn("1111", matches)
1202 self.assertIn("1234", matches)
1210 self.assertIn("1234", matches)
1203 self.assertNotIn("1999", matches)
1211 self.assertNotIn("1999", matches)
1204 self.assertNotIn("'1999'", matches)
1212 self.assertNotIn("'1999'", matches)
1205
1213
1206 _, matches = complete(line_buffer="d[0xdead")
1214 _, matches = complete(line_buffer="d[0xdead")
1207 self.assertIn("0xdeadbeef", matches)
1215 self.assertIn("0xdeadbeef", matches)
1208
1216
1209 _, matches = complete(line_buffer="d[2")
1217 _, matches = complete(line_buffer="d[2")
1210 self.assertIn("21", matches)
1218 self.assertIn("21", matches)
1211 self.assertIn("22", matches)
1219 self.assertIn("22", matches)
1212
1220
1213 _, matches = complete(line_buffer="d[0b101")
1221 _, matches = complete(line_buffer="d[0b101")
1214 self.assertIn("0b10101", matches)
1222 self.assertIn("0b10101", matches)
1215 self.assertIn("0b10110", matches)
1223 self.assertIn("0b10110", matches)
1216
1224
1217 def test_dict_key_completion_contexts(self):
1225 def test_dict_key_completion_contexts(self):
1218 """Test expression contexts in which dict key completion occurs"""
1226 """Test expression contexts in which dict key completion occurs"""
1219 ip = get_ipython()
1227 ip = get_ipython()
1220 complete = ip.Completer.complete
1228 complete = ip.Completer.complete
1221 d = {"abc": None}
1229 d = {"abc": None}
1222 ip.user_ns["d"] = d
1230 ip.user_ns["d"] = d
1223
1231
1224 class C:
1232 class C:
1225 data = d
1233 data = d
1226
1234
1227 ip.user_ns["C"] = C
1235 ip.user_ns["C"] = C
1228 ip.user_ns["get"] = lambda: d
1236 ip.user_ns["get"] = lambda: d
1229 ip.user_ns["nested"] = {"x": d}
1237 ip.user_ns["nested"] = {"x": d}
1230
1238
1231 def assert_no_completion(**kwargs):
1239 def assert_no_completion(**kwargs):
1232 _, matches = complete(**kwargs)
1240 _, matches = complete(**kwargs)
1233 self.assertNotIn("abc", matches)
1241 self.assertNotIn("abc", matches)
1234 self.assertNotIn("abc'", matches)
1242 self.assertNotIn("abc'", matches)
1235 self.assertNotIn("abc']", matches)
1243 self.assertNotIn("abc']", matches)
1236 self.assertNotIn("'abc'", matches)
1244 self.assertNotIn("'abc'", matches)
1237 self.assertNotIn("'abc']", matches)
1245 self.assertNotIn("'abc']", matches)
1238
1246
1239 def assert_completion(**kwargs):
1247 def assert_completion(**kwargs):
1240 _, matches = complete(**kwargs)
1248 _, matches = complete(**kwargs)
1241 self.assertIn("'abc'", matches)
1249 self.assertIn("'abc'", matches)
1242 self.assertNotIn("'abc']", matches)
1250 self.assertNotIn("'abc']", matches)
1243
1251
1244 # no completion after string closed, even if reopened
1252 # no completion after string closed, even if reopened
1245 assert_no_completion(line_buffer="d['a'")
1253 assert_no_completion(line_buffer="d['a'")
1246 assert_no_completion(line_buffer='d["a"')
1254 assert_no_completion(line_buffer='d["a"')
1247 assert_no_completion(line_buffer="d['a' + ")
1255 assert_no_completion(line_buffer="d['a' + ")
1248 assert_no_completion(line_buffer="d['a' + '")
1256 assert_no_completion(line_buffer="d['a' + '")
1249
1257
1250 # completion in non-trivial expressions
1258 # completion in non-trivial expressions
1251 assert_completion(line_buffer="+ d[")
1259 assert_completion(line_buffer="+ d[")
1252 assert_completion(line_buffer="(d[")
1260 assert_completion(line_buffer="(d[")
1253 assert_completion(line_buffer="C.data[")
1261 assert_completion(line_buffer="C.data[")
1254
1262
1255 # nested dict completion
1263 # nested dict completion
1256 assert_completion(line_buffer="nested['x'][")
1264 assert_completion(line_buffer="nested['x'][")
1257
1265
1258 with evaluation_policy("minimal"):
1266 with evaluation_policy("minimal"):
1259 with pytest.raises(AssertionError):
1267 with pytest.raises(AssertionError):
1260 assert_completion(line_buffer="nested['x'][")
1268 assert_completion(line_buffer="nested['x'][")
1261
1269
1262 # greedy flag
1270 # greedy flag
1263 def assert_completion(**kwargs):
1271 def assert_completion(**kwargs):
1264 _, matches = complete(**kwargs)
1272 _, matches = complete(**kwargs)
1265 self.assertIn("get()['abc']", matches)
1273 self.assertIn("get()['abc']", matches)
1266
1274
1267 assert_no_completion(line_buffer="get()[")
1275 assert_no_completion(line_buffer="get()[")
1268 with greedy_completion():
1276 with greedy_completion():
1269 assert_completion(line_buffer="get()[")
1277 assert_completion(line_buffer="get()[")
1270 assert_completion(line_buffer="get()['")
1278 assert_completion(line_buffer="get()['")
1271 assert_completion(line_buffer="get()['a")
1279 assert_completion(line_buffer="get()['a")
1272 assert_completion(line_buffer="get()['ab")
1280 assert_completion(line_buffer="get()['ab")
1273 assert_completion(line_buffer="get()['abc")
1281 assert_completion(line_buffer="get()['abc")
1274
1282
1275 def test_dict_key_completion_bytes(self):
1283 def test_dict_key_completion_bytes(self):
1276 """Test handling of bytes in dict key completion"""
1284 """Test handling of bytes in dict key completion"""
1277 ip = get_ipython()
1285 ip = get_ipython()
1278 complete = ip.Completer.complete
1286 complete = ip.Completer.complete
1279
1287
1280 ip.user_ns["d"] = {"abc": None, b"abd": None}
1288 ip.user_ns["d"] = {"abc": None, b"abd": None}
1281
1289
1282 _, matches = complete(line_buffer="d[")
1290 _, matches = complete(line_buffer="d[")
1283 self.assertIn("'abc'", matches)
1291 self.assertIn("'abc'", matches)
1284 self.assertIn("b'abd'", matches)
1292 self.assertIn("b'abd'", matches)
1285
1293
1286 if False: # not currently implemented
1294 if False: # not currently implemented
1287 _, matches = complete(line_buffer="d[b")
1295 _, matches = complete(line_buffer="d[b")
1288 self.assertIn("b'abd'", matches)
1296 self.assertIn("b'abd'", matches)
1289 self.assertNotIn("b'abc'", matches)
1297 self.assertNotIn("b'abc'", matches)
1290
1298
1291 _, matches = complete(line_buffer="d[b'")
1299 _, matches = complete(line_buffer="d[b'")
1292 self.assertIn("abd", matches)
1300 self.assertIn("abd", matches)
1293 self.assertNotIn("abc", matches)
1301 self.assertNotIn("abc", matches)
1294
1302
1295 _, matches = complete(line_buffer="d[B'")
1303 _, matches = complete(line_buffer="d[B'")
1296 self.assertIn("abd", matches)
1304 self.assertIn("abd", matches)
1297 self.assertNotIn("abc", matches)
1305 self.assertNotIn("abc", matches)
1298
1306
1299 _, matches = complete(line_buffer="d['")
1307 _, matches = complete(line_buffer="d['")
1300 self.assertIn("abc", matches)
1308 self.assertIn("abc", matches)
1301 self.assertNotIn("abd", matches)
1309 self.assertNotIn("abd", matches)
1302
1310
1303 def test_dict_key_completion_unicode_py3(self):
1311 def test_dict_key_completion_unicode_py3(self):
1304 """Test handling of unicode in dict key completion"""
1312 """Test handling of unicode in dict key completion"""
1305 ip = get_ipython()
1313 ip = get_ipython()
1306 complete = ip.Completer.complete
1314 complete = ip.Completer.complete
1307
1315
1308 ip.user_ns["d"] = {"a\u05d0": None}
1316 ip.user_ns["d"] = {"a\u05d0": None}
1309
1317
1310 # query using escape
1318 # query using escape
1311 if sys.platform != "win32":
1319 if sys.platform != "win32":
1312 # Known failure on Windows
1320 # Known failure on Windows
1313 _, matches = complete(line_buffer="d['a\\u05d0")
1321 _, matches = complete(line_buffer="d['a\\u05d0")
1314 self.assertIn("u05d0", matches) # tokenized after \\
1322 self.assertIn("u05d0", matches) # tokenized after \\
1315
1323
1316 # query using character
1324 # query using character
1317 _, matches = complete(line_buffer="d['a\u05d0")
1325 _, matches = complete(line_buffer="d['a\u05d0")
1318 self.assertIn("a\u05d0", matches)
1326 self.assertIn("a\u05d0", matches)
1319
1327
1320 with greedy_completion():
1328 with greedy_completion():
1321 # query using escape
1329 # query using escape
1322 _, matches = complete(line_buffer="d['a\\u05d0")
1330 _, matches = complete(line_buffer="d['a\\u05d0")
1323 self.assertIn("d['a\\u05d0']", matches) # tokenized after \\
1331 self.assertIn("d['a\\u05d0']", matches) # tokenized after \\
1324
1332
1325 # query using character
1333 # query using character
1326 _, matches = complete(line_buffer="d['a\u05d0")
1334 _, matches = complete(line_buffer="d['a\u05d0")
1327 self.assertIn("d['a\u05d0']", matches)
1335 self.assertIn("d['a\u05d0']", matches)
1328
1336
1329 @dec.skip_without("numpy")
1337 @dec.skip_without("numpy")
1330 def test_struct_array_key_completion(self):
1338 def test_struct_array_key_completion(self):
1331 """Test dict key completion applies to numpy struct arrays"""
1339 """Test dict key completion applies to numpy struct arrays"""
1332 import numpy
1340 import numpy
1333
1341
1334 ip = get_ipython()
1342 ip = get_ipython()
1335 complete = ip.Completer.complete
1343 complete = ip.Completer.complete
1336 ip.user_ns["d"] = numpy.array([], dtype=[("hello", "f"), ("world", "f")])
1344 ip.user_ns["d"] = numpy.array([], dtype=[("hello", "f"), ("world", "f")])
1337 _, matches = complete(line_buffer="d['")
1345 _, matches = complete(line_buffer="d['")
1338 self.assertIn("hello", matches)
1346 self.assertIn("hello", matches)
1339 self.assertIn("world", matches)
1347 self.assertIn("world", matches)
1340 # complete on the numpy struct itself
1348 # complete on the numpy struct itself
1341 dt = numpy.dtype(
1349 dt = numpy.dtype(
1342 [("my_head", [("my_dt", ">u4"), ("my_df", ">u4")]), ("my_data", ">f4", 5)]
1350 [("my_head", [("my_dt", ">u4"), ("my_df", ">u4")]), ("my_data", ">f4", 5)]
1343 )
1351 )
1344 x = numpy.zeros(2, dtype=dt)
1352 x = numpy.zeros(2, dtype=dt)
1345 ip.user_ns["d"] = x[1]
1353 ip.user_ns["d"] = x[1]
1346 _, matches = complete(line_buffer="d['")
1354 _, matches = complete(line_buffer="d['")
1347 self.assertIn("my_head", matches)
1355 self.assertIn("my_head", matches)
1348 self.assertIn("my_data", matches)
1356 self.assertIn("my_data", matches)
1349
1357
1350 def completes_on_nested():
1358 def completes_on_nested():
1351 ip.user_ns["d"] = numpy.zeros(2, dtype=dt)
1359 ip.user_ns["d"] = numpy.zeros(2, dtype=dt)
1352 _, matches = complete(line_buffer="d[1]['my_head']['")
1360 _, matches = complete(line_buffer="d[1]['my_head']['")
1353 self.assertTrue(any(["my_dt" in m for m in matches]))
1361 self.assertTrue(any(["my_dt" in m for m in matches]))
1354 self.assertTrue(any(["my_df" in m for m in matches]))
1362 self.assertTrue(any(["my_df" in m for m in matches]))
1355 # complete on a nested level
1363 # complete on a nested level
1356 with greedy_completion():
1364 with greedy_completion():
1357 completes_on_nested()
1365 completes_on_nested()
1358
1366
1359 with evaluation_policy("limited"):
1367 with evaluation_policy("limited"):
1360 completes_on_nested()
1368 completes_on_nested()
1361
1369
1362 with evaluation_policy("minimal"):
1370 with evaluation_policy("minimal"):
1363 with pytest.raises(AssertionError):
1371 with pytest.raises(AssertionError):
1364 completes_on_nested()
1372 completes_on_nested()
1365
1373
1366 @dec.skip_without("pandas")
1374 @dec.skip_without("pandas")
1367 def test_dataframe_key_completion(self):
1375 def test_dataframe_key_completion(self):
1368 """Test dict key completion applies to pandas DataFrames"""
1376 """Test dict key completion applies to pandas DataFrames"""
1369 import pandas
1377 import pandas
1370
1378
1371 ip = get_ipython()
1379 ip = get_ipython()
1372 complete = ip.Completer.complete
1380 complete = ip.Completer.complete
1373 ip.user_ns["d"] = pandas.DataFrame({"hello": [1], "world": [2]})
1381 ip.user_ns["d"] = pandas.DataFrame({"hello": [1], "world": [2]})
1374 _, matches = complete(line_buffer="d['")
1382 _, matches = complete(line_buffer="d['")
1375 self.assertIn("hello", matches)
1383 self.assertIn("hello", matches)
1376 self.assertIn("world", matches)
1384 self.assertIn("world", matches)
1377 _, matches = complete(line_buffer="d.loc[:, '")
1385 _, matches = complete(line_buffer="d.loc[:, '")
1378 self.assertIn("hello", matches)
1386 self.assertIn("hello", matches)
1379 self.assertIn("world", matches)
1387 self.assertIn("world", matches)
1380 _, matches = complete(line_buffer="d.loc[1:, '")
1388 _, matches = complete(line_buffer="d.loc[1:, '")
1381 self.assertIn("hello", matches)
1389 self.assertIn("hello", matches)
1382 _, matches = complete(line_buffer="d.loc[1:1, '")
1390 _, matches = complete(line_buffer="d.loc[1:1, '")
1383 self.assertIn("hello", matches)
1391 self.assertIn("hello", matches)
1384 _, matches = complete(line_buffer="d.loc[1:1:-1, '")
1392 _, matches = complete(line_buffer="d.loc[1:1:-1, '")
1385 self.assertIn("hello", matches)
1393 self.assertIn("hello", matches)
1386 _, matches = complete(line_buffer="d.loc[::, '")
1394 _, matches = complete(line_buffer="d.loc[::, '")
1387 self.assertIn("hello", matches)
1395 self.assertIn("hello", matches)
1388
1396
1389 def test_dict_key_completion_invalids(self):
1397 def test_dict_key_completion_invalids(self):
1390 """Smoke test cases dict key completion can't handle"""
1398 """Smoke test cases dict key completion can't handle"""
1391 ip = get_ipython()
1399 ip = get_ipython()
1392 complete = ip.Completer.complete
1400 complete = ip.Completer.complete
1393
1401
1394 ip.user_ns["no_getitem"] = None
1402 ip.user_ns["no_getitem"] = None
1395 ip.user_ns["no_keys"] = []
1403 ip.user_ns["no_keys"] = []
1396 ip.user_ns["cant_call_keys"] = dict
1404 ip.user_ns["cant_call_keys"] = dict
1397 ip.user_ns["empty"] = {}
1405 ip.user_ns["empty"] = {}
1398 ip.user_ns["d"] = {"abc": 5}
1406 ip.user_ns["d"] = {"abc": 5}
1399
1407
1400 _, matches = complete(line_buffer="no_getitem['")
1408 _, matches = complete(line_buffer="no_getitem['")
1401 _, matches = complete(line_buffer="no_keys['")
1409 _, matches = complete(line_buffer="no_keys['")
1402 _, matches = complete(line_buffer="cant_call_keys['")
1410 _, matches = complete(line_buffer="cant_call_keys['")
1403 _, matches = complete(line_buffer="empty['")
1411 _, matches = complete(line_buffer="empty['")
1404 _, matches = complete(line_buffer="name_error['")
1412 _, matches = complete(line_buffer="name_error['")
1405 _, matches = complete(line_buffer="d['\\") # incomplete escape
1413 _, matches = complete(line_buffer="d['\\") # incomplete escape
1406
1414
1407 def test_object_key_completion(self):
1415 def test_object_key_completion(self):
1408 ip = get_ipython()
1416 ip = get_ipython()
1409 ip.user_ns["key_completable"] = KeyCompletable(["qwerty", "qwick"])
1417 ip.user_ns["key_completable"] = KeyCompletable(["qwerty", "qwick"])
1410
1418
1411 _, matches = ip.Completer.complete(line_buffer="key_completable['qw")
1419 _, matches = ip.Completer.complete(line_buffer="key_completable['qw")
1412 self.assertIn("qwerty", matches)
1420 self.assertIn("qwerty", matches)
1413 self.assertIn("qwick", matches)
1421 self.assertIn("qwick", matches)
1414
1422
1415 def test_class_key_completion(self):
1423 def test_class_key_completion(self):
1416 ip = get_ipython()
1424 ip = get_ipython()
1417 NamedInstanceClass("qwerty")
1425 NamedInstanceClass("qwerty")
1418 NamedInstanceClass("qwick")
1426 NamedInstanceClass("qwick")
1419 ip.user_ns["named_instance_class"] = NamedInstanceClass
1427 ip.user_ns["named_instance_class"] = NamedInstanceClass
1420
1428
1421 _, matches = ip.Completer.complete(line_buffer="named_instance_class['qw")
1429 _, matches = ip.Completer.complete(line_buffer="named_instance_class['qw")
1422 self.assertIn("qwerty", matches)
1430 self.assertIn("qwerty", matches)
1423 self.assertIn("qwick", matches)
1431 self.assertIn("qwick", matches)
1424
1432
1425 def test_tryimport(self):
1433 def test_tryimport(self):
1426 """
1434 """
1427 Test that try-import don't crash on trailing dot, and import modules before
1435 Test that try-import don't crash on trailing dot, and import modules before
1428 """
1436 """
1429 from IPython.core.completerlib import try_import
1437 from IPython.core.completerlib import try_import
1430
1438
1431 assert try_import("IPython.")
1439 assert try_import("IPython.")
1432
1440
1433 def test_aimport_module_completer(self):
1441 def test_aimport_module_completer(self):
1434 ip = get_ipython()
1442 ip = get_ipython()
1435 _, matches = ip.complete("i", "%aimport i")
1443 _, matches = ip.complete("i", "%aimport i")
1436 self.assertIn("io", matches)
1444 self.assertIn("io", matches)
1437 self.assertNotIn("int", matches)
1445 self.assertNotIn("int", matches)
1438
1446
1439 def test_nested_import_module_completer(self):
1447 def test_nested_import_module_completer(self):
1440 ip = get_ipython()
1448 ip = get_ipython()
1441 _, matches = ip.complete(None, "import IPython.co", 17)
1449 _, matches = ip.complete(None, "import IPython.co", 17)
1442 self.assertIn("IPython.core", matches)
1450 self.assertIn("IPython.core", matches)
1443 self.assertNotIn("import IPython.core", matches)
1451 self.assertNotIn("import IPython.core", matches)
1444 self.assertNotIn("IPython.display", matches)
1452 self.assertNotIn("IPython.display", matches)
1445
1453
1446 def test_import_module_completer(self):
1454 def test_import_module_completer(self):
1447 ip = get_ipython()
1455 ip = get_ipython()
1448 _, matches = ip.complete("i", "import i")
1456 _, matches = ip.complete("i", "import i")
1449 self.assertIn("io", matches)
1457 self.assertIn("io", matches)
1450 self.assertNotIn("int", matches)
1458 self.assertNotIn("int", matches)
1451
1459
1452 def test_from_module_completer(self):
1460 def test_from_module_completer(self):
1453 ip = get_ipython()
1461 ip = get_ipython()
1454 _, matches = ip.complete("B", "from io import B", 16)
1462 _, matches = ip.complete("B", "from io import B", 16)
1455 self.assertIn("BytesIO", matches)
1463 self.assertIn("BytesIO", matches)
1456 self.assertNotIn("BaseException", matches)
1464 self.assertNotIn("BaseException", matches)
1457
1465
1458 def test_snake_case_completion(self):
1466 def test_snake_case_completion(self):
1459 ip = get_ipython()
1467 ip = get_ipython()
1460 ip.Completer.use_jedi = False
1468 ip.Completer.use_jedi = False
1461 ip.user_ns["some_three"] = 3
1469 ip.user_ns["some_three"] = 3
1462 ip.user_ns["some_four"] = 4
1470 ip.user_ns["some_four"] = 4
1463 _, matches = ip.complete("s_", "print(s_f")
1471 _, matches = ip.complete("s_", "print(s_f")
1464 self.assertIn("some_three", matches)
1472 self.assertIn("some_three", matches)
1465 self.assertIn("some_four", matches)
1473 self.assertIn("some_four", matches)
1466
1474
1467 def test_mix_terms(self):
1475 def test_mix_terms(self):
1468 ip = get_ipython()
1476 ip = get_ipython()
1469 from textwrap import dedent
1477 from textwrap import dedent
1470
1478
1471 ip.Completer.use_jedi = False
1479 ip.Completer.use_jedi = False
1472 ip.ex(
1480 ip.ex(
1473 dedent(
1481 dedent(
1474 """
1482 """
1475 class Test:
1483 class Test:
1476 def meth(self, meth_arg1):
1484 def meth(self, meth_arg1):
1477 print("meth")
1485 print("meth")
1478
1486
1479 def meth_1(self, meth1_arg1, meth1_arg2):
1487 def meth_1(self, meth1_arg1, meth1_arg2):
1480 print("meth1")
1488 print("meth1")
1481
1489
1482 def meth_2(self, meth2_arg1, meth2_arg2):
1490 def meth_2(self, meth2_arg1, meth2_arg2):
1483 print("meth2")
1491 print("meth2")
1484 test = Test()
1492 test = Test()
1485 """
1493 """
1486 )
1494 )
1487 )
1495 )
1488 _, matches = ip.complete(None, "test.meth(")
1496 _, matches = ip.complete(None, "test.meth(")
1489 self.assertIn("meth_arg1=", matches)
1497 self.assertIn("meth_arg1=", matches)
1490 self.assertNotIn("meth2_arg1=", matches)
1498 self.assertNotIn("meth2_arg1=", matches)
1491
1499
1492 def test_percent_symbol_restrict_to_magic_completions(self):
1500 def test_percent_symbol_restrict_to_magic_completions(self):
1493 ip = get_ipython()
1501 ip = get_ipython()
1494 completer = ip.Completer
1502 completer = ip.Completer
1495 text = "%a"
1503 text = "%a"
1496
1504
1497 with provisionalcompleter():
1505 with provisionalcompleter():
1498 completer.use_jedi = True
1506 completer.use_jedi = True
1499 completions = completer.completions(text, len(text))
1507 completions = completer.completions(text, len(text))
1500 for c in completions:
1508 for c in completions:
1501 self.assertEqual(c.text[0], "%")
1509 self.assertEqual(c.text[0], "%")
1502
1510
1503 def test_fwd_unicode_restricts(self):
1511 def test_fwd_unicode_restricts(self):
1504 ip = get_ipython()
1512 ip = get_ipython()
1505 completer = ip.Completer
1513 completer = ip.Completer
1506 text = "\\ROMAN NUMERAL FIVE"
1514 text = "\\ROMAN NUMERAL FIVE"
1507
1515
1508 with provisionalcompleter():
1516 with provisionalcompleter():
1509 completer.use_jedi = True
1517 completer.use_jedi = True
1510 completions = [
1518 completions = [
1511 completion.text for completion in completer.completions(text, len(text))
1519 completion.text for completion in completer.completions(text, len(text))
1512 ]
1520 ]
1513 self.assertEqual(completions, ["\u2164"])
1521 self.assertEqual(completions, ["\u2164"])
1514
1522
1515 def test_dict_key_restrict_to_dicts(self):
1523 def test_dict_key_restrict_to_dicts(self):
1516 """Test that dict key suppresses non-dict completion items"""
1524 """Test that dict key suppresses non-dict completion items"""
1517 ip = get_ipython()
1525 ip = get_ipython()
1518 c = ip.Completer
1526 c = ip.Completer
1519 d = {"abc": None}
1527 d = {"abc": None}
1520 ip.user_ns["d"] = d
1528 ip.user_ns["d"] = d
1521
1529
1522 text = 'd["a'
1530 text = 'd["a'
1523
1531
1524 def _():
1532 def _():
1525 with provisionalcompleter():
1533 with provisionalcompleter():
1526 c.use_jedi = True
1534 c.use_jedi = True
1527 return [
1535 return [
1528 completion.text for completion in c.completions(text, len(text))
1536 completion.text for completion in c.completions(text, len(text))
1529 ]
1537 ]
1530
1538
1531 completions = _()
1539 completions = _()
1532 self.assertEqual(completions, ["abc"])
1540 self.assertEqual(completions, ["abc"])
1533
1541
1534 # check that it can be disabled in granular manner:
1542 # check that it can be disabled in granular manner:
1535 cfg = Config()
1543 cfg = Config()
1536 cfg.IPCompleter.suppress_competing_matchers = {
1544 cfg.IPCompleter.suppress_competing_matchers = {
1537 "IPCompleter.dict_key_matcher": False
1545 "IPCompleter.dict_key_matcher": False
1538 }
1546 }
1539 c.update_config(cfg)
1547 c.update_config(cfg)
1540
1548
1541 completions = _()
1549 completions = _()
1542 self.assertIn("abc", completions)
1550 self.assertIn("abc", completions)
1543 self.assertGreater(len(completions), 1)
1551 self.assertGreater(len(completions), 1)
1544
1552
1545 def test_matcher_suppression(self):
1553 def test_matcher_suppression(self):
1546 @completion_matcher(identifier="a_matcher")
1554 @completion_matcher(identifier="a_matcher")
1547 def a_matcher(text):
1555 def a_matcher(text):
1548 return ["completion_a"]
1556 return ["completion_a"]
1549
1557
1550 @completion_matcher(identifier="b_matcher", api_version=2)
1558 @completion_matcher(identifier="b_matcher", api_version=2)
1551 def b_matcher(context: CompletionContext):
1559 def b_matcher(context: CompletionContext):
1552 text = context.token
1560 text = context.token
1553 result = {"completions": [SimpleCompletion("completion_b")]}
1561 result = {"completions": [SimpleCompletion("completion_b")]}
1554
1562
1555 if text == "suppress c":
1563 if text == "suppress c":
1556 result["suppress"] = {"c_matcher"}
1564 result["suppress"] = {"c_matcher"}
1557
1565
1558 if text.startswith("suppress all"):
1566 if text.startswith("suppress all"):
1559 result["suppress"] = True
1567 result["suppress"] = True
1560 if text == "suppress all but c":
1568 if text == "suppress all but c":
1561 result["do_not_suppress"] = {"c_matcher"}
1569 result["do_not_suppress"] = {"c_matcher"}
1562 if text == "suppress all but a":
1570 if text == "suppress all but a":
1563 result["do_not_suppress"] = {"a_matcher"}
1571 result["do_not_suppress"] = {"a_matcher"}
1564
1572
1565 return result
1573 return result
1566
1574
1567 @completion_matcher(identifier="c_matcher")
1575 @completion_matcher(identifier="c_matcher")
1568 def c_matcher(text):
1576 def c_matcher(text):
1569 return ["completion_c"]
1577 return ["completion_c"]
1570
1578
1571 with custom_matchers([a_matcher, b_matcher, c_matcher]):
1579 with custom_matchers([a_matcher, b_matcher, c_matcher]):
1572 ip = get_ipython()
1580 ip = get_ipython()
1573 c = ip.Completer
1581 c = ip.Completer
1574
1582
1575 def _(text, expected):
1583 def _(text, expected):
1576 c.use_jedi = False
1584 c.use_jedi = False
1577 s, matches = c.complete(text)
1585 s, matches = c.complete(text)
1578 self.assertEqual(expected, matches)
1586 self.assertEqual(expected, matches)
1579
1587
1580 _("do not suppress", ["completion_a", "completion_b", "completion_c"])
1588 _("do not suppress", ["completion_a", "completion_b", "completion_c"])
1581 _("suppress all", ["completion_b"])
1589 _("suppress all", ["completion_b"])
1582 _("suppress all but a", ["completion_a", "completion_b"])
1590 _("suppress all but a", ["completion_a", "completion_b"])
1583 _("suppress all but c", ["completion_b", "completion_c"])
1591 _("suppress all but c", ["completion_b", "completion_c"])
1584
1592
1585 def configure(suppression_config):
1593 def configure(suppression_config):
1586 cfg = Config()
1594 cfg = Config()
1587 cfg.IPCompleter.suppress_competing_matchers = suppression_config
1595 cfg.IPCompleter.suppress_competing_matchers = suppression_config
1588 c.update_config(cfg)
1596 c.update_config(cfg)
1589
1597
1590 # test that configuration takes priority over the run-time decisions
1598 # test that configuration takes priority over the run-time decisions
1591
1599
1592 configure(False)
1600 configure(False)
1593 _("suppress all", ["completion_a", "completion_b", "completion_c"])
1601 _("suppress all", ["completion_a", "completion_b", "completion_c"])
1594
1602
1595 configure({"b_matcher": False})
1603 configure({"b_matcher": False})
1596 _("suppress all", ["completion_a", "completion_b", "completion_c"])
1604 _("suppress all", ["completion_a", "completion_b", "completion_c"])
1597
1605
1598 configure({"a_matcher": False})
1606 configure({"a_matcher": False})
1599 _("suppress all", ["completion_b"])
1607 _("suppress all", ["completion_b"])
1600
1608
1601 configure({"b_matcher": True})
1609 configure({"b_matcher": True})
1602 _("do not suppress", ["completion_b"])
1610 _("do not suppress", ["completion_b"])
1603
1611
1604 configure(True)
1612 configure(True)
1605 _("do not suppress", ["completion_a"])
1613 _("do not suppress", ["completion_a"])
1606
1614
1607 def test_matcher_suppression_with_iterator(self):
1615 def test_matcher_suppression_with_iterator(self):
1608 @completion_matcher(identifier="matcher_returning_iterator")
1616 @completion_matcher(identifier="matcher_returning_iterator")
1609 def matcher_returning_iterator(text):
1617 def matcher_returning_iterator(text):
1610 return iter(["completion_iter"])
1618 return iter(["completion_iter"])
1611
1619
1612 @completion_matcher(identifier="matcher_returning_list")
1620 @completion_matcher(identifier="matcher_returning_list")
1613 def matcher_returning_list(text):
1621 def matcher_returning_list(text):
1614 return ["completion_list"]
1622 return ["completion_list"]
1615
1623
1616 with custom_matchers([matcher_returning_iterator, matcher_returning_list]):
1624 with custom_matchers([matcher_returning_iterator, matcher_returning_list]):
1617 ip = get_ipython()
1625 ip = get_ipython()
1618 c = ip.Completer
1626 c = ip.Completer
1619
1627
1620 def _(text, expected):
1628 def _(text, expected):
1621 c.use_jedi = False
1629 c.use_jedi = False
1622 s, matches = c.complete(text)
1630 s, matches = c.complete(text)
1623 self.assertEqual(expected, matches)
1631 self.assertEqual(expected, matches)
1624
1632
1625 def configure(suppression_config):
1633 def configure(suppression_config):
1626 cfg = Config()
1634 cfg = Config()
1627 cfg.IPCompleter.suppress_competing_matchers = suppression_config
1635 cfg.IPCompleter.suppress_competing_matchers = suppression_config
1628 c.update_config(cfg)
1636 c.update_config(cfg)
1629
1637
1630 configure(False)
1638 configure(False)
1631 _("---", ["completion_iter", "completion_list"])
1639 _("---", ["completion_iter", "completion_list"])
1632
1640
1633 configure(True)
1641 configure(True)
1634 _("---", ["completion_iter"])
1642 _("---", ["completion_iter"])
1635
1643
1636 configure(None)
1644 configure(None)
1637 _("--", ["completion_iter", "completion_list"])
1645 _("--", ["completion_iter", "completion_list"])
1638
1646
1639 @pytest.mark.xfail(
1647 @pytest.mark.xfail(
1640 sys.version_info.releaselevel in ("alpha",),
1648 sys.version_info.releaselevel in ("alpha",),
1641 reason="Parso does not yet parse 3.13",
1649 reason="Parso does not yet parse 3.13",
1642 )
1650 )
1643 def test_matcher_suppression_with_jedi(self):
1651 def test_matcher_suppression_with_jedi(self):
1644 ip = get_ipython()
1652 ip = get_ipython()
1645 c = ip.Completer
1653 c = ip.Completer
1646 c.use_jedi = True
1654 c.use_jedi = True
1647
1655
1648 def configure(suppression_config):
1656 def configure(suppression_config):
1649 cfg = Config()
1657 cfg = Config()
1650 cfg.IPCompleter.suppress_competing_matchers = suppression_config
1658 cfg.IPCompleter.suppress_competing_matchers = suppression_config
1651 c.update_config(cfg)
1659 c.update_config(cfg)
1652
1660
1653 def _():
1661 def _():
1654 with provisionalcompleter():
1662 with provisionalcompleter():
1655 matches = [completion.text for completion in c.completions("dict.", 5)]
1663 matches = [completion.text for completion in c.completions("dict.", 5)]
1656 self.assertIn("keys", matches)
1664 self.assertIn("keys", matches)
1657
1665
1658 configure(False)
1666 configure(False)
1659 _()
1667 _()
1660
1668
1661 configure(True)
1669 configure(True)
1662 _()
1670 _()
1663
1671
1664 configure(None)
1672 configure(None)
1665 _()
1673 _()
1666
1674
1667 def test_matcher_disabling(self):
1675 def test_matcher_disabling(self):
1668 @completion_matcher(identifier="a_matcher")
1676 @completion_matcher(identifier="a_matcher")
1669 def a_matcher(text):
1677 def a_matcher(text):
1670 return ["completion_a"]
1678 return ["completion_a"]
1671
1679
1672 @completion_matcher(identifier="b_matcher")
1680 @completion_matcher(identifier="b_matcher")
1673 def b_matcher(text):
1681 def b_matcher(text):
1674 return ["completion_b"]
1682 return ["completion_b"]
1675
1683
1676 def _(expected):
1684 def _(expected):
1677 s, matches = c.complete("completion_")
1685 s, matches = c.complete("completion_")
1678 self.assertEqual(expected, matches)
1686 self.assertEqual(expected, matches)
1679
1687
1680 with custom_matchers([a_matcher, b_matcher]):
1688 with custom_matchers([a_matcher, b_matcher]):
1681 ip = get_ipython()
1689 ip = get_ipython()
1682 c = ip.Completer
1690 c = ip.Completer
1683
1691
1684 _(["completion_a", "completion_b"])
1692 _(["completion_a", "completion_b"])
1685
1693
1686 cfg = Config()
1694 cfg = Config()
1687 cfg.IPCompleter.disable_matchers = ["b_matcher"]
1695 cfg.IPCompleter.disable_matchers = ["b_matcher"]
1688 c.update_config(cfg)
1696 c.update_config(cfg)
1689
1697
1690 _(["completion_a"])
1698 _(["completion_a"])
1691
1699
1692 cfg.IPCompleter.disable_matchers = []
1700 cfg.IPCompleter.disable_matchers = []
1693 c.update_config(cfg)
1701 c.update_config(cfg)
1694
1702
1695 def test_matcher_priority(self):
1703 def test_matcher_priority(self):
1696 @completion_matcher(identifier="a_matcher", priority=0, api_version=2)
1704 @completion_matcher(identifier="a_matcher", priority=0, api_version=2)
1697 def a_matcher(text):
1705 def a_matcher(text):
1698 return {"completions": [SimpleCompletion("completion_a")], "suppress": True}
1706 return {"completions": [SimpleCompletion("completion_a")], "suppress": True}
1699
1707
1700 @completion_matcher(identifier="b_matcher", priority=2, api_version=2)
1708 @completion_matcher(identifier="b_matcher", priority=2, api_version=2)
1701 def b_matcher(text):
1709 def b_matcher(text):
1702 return {"completions": [SimpleCompletion("completion_b")], "suppress": True}
1710 return {"completions": [SimpleCompletion("completion_b")], "suppress": True}
1703
1711
1704 def _(expected):
1712 def _(expected):
1705 s, matches = c.complete("completion_")
1713 s, matches = c.complete("completion_")
1706 self.assertEqual(expected, matches)
1714 self.assertEqual(expected, matches)
1707
1715
1708 with custom_matchers([a_matcher, b_matcher]):
1716 with custom_matchers([a_matcher, b_matcher]):
1709 ip = get_ipython()
1717 ip = get_ipython()
1710 c = ip.Completer
1718 c = ip.Completer
1711
1719
1712 _(["completion_b"])
1720 _(["completion_b"])
1713 a_matcher.matcher_priority = 3
1721 a_matcher.matcher_priority = 3
1714 _(["completion_a"])
1722 _(["completion_a"])
1715
1723
1716
1724
1717 @pytest.mark.parametrize(
1725 @pytest.mark.parametrize(
1718 "input, expected",
1726 "input, expected",
1719 [
1727 [
1720 ["1.234", "1.234"],
1728 ["1.234", "1.234"],
1721 # should match signed numbers
1729 # should match signed numbers
1722 ["+1", "+1"],
1730 ["+1", "+1"],
1723 ["-1", "-1"],
1731 ["-1", "-1"],
1724 ["-1.0", "-1.0"],
1732 ["-1.0", "-1.0"],
1725 ["-1.", "-1."],
1733 ["-1.", "-1."],
1726 ["+1.", "+1."],
1734 ["+1.", "+1."],
1727 [".1", ".1"],
1735 [".1", ".1"],
1728 # should not match non-numbers
1736 # should not match non-numbers
1729 ["1..", None],
1737 ["1..", None],
1730 ["..", None],
1738 ["..", None],
1731 [".1.", None],
1739 [".1.", None],
1732 # should match after comma
1740 # should match after comma
1733 [",1", "1"],
1741 [",1", "1"],
1734 [", 1", "1"],
1742 [", 1", "1"],
1735 [", .1", ".1"],
1743 [", .1", ".1"],
1736 [", +.1", "+.1"],
1744 [", +.1", "+.1"],
1737 # should not match after trailing spaces
1745 # should not match after trailing spaces
1738 [".1 ", None],
1746 [".1 ", None],
1739 # some complex cases
1747 # some complex cases
1740 ["0b_0011_1111_0100_1110", "0b_0011_1111_0100_1110"],
1748 ["0b_0011_1111_0100_1110", "0b_0011_1111_0100_1110"],
1741 ["0xdeadbeef", "0xdeadbeef"],
1749 ["0xdeadbeef", "0xdeadbeef"],
1742 ["0b_1110_0101", "0b_1110_0101"],
1750 ["0b_1110_0101", "0b_1110_0101"],
1743 # should not match if in an operation
1751 # should not match if in an operation
1744 ["1 + 1", None],
1752 ["1 + 1", None],
1745 [", 1 + 1", None],
1753 [", 1 + 1", None],
1746 ],
1754 ],
1747 )
1755 )
1748 def test_match_numeric_literal_for_dict_key(input, expected):
1756 def test_match_numeric_literal_for_dict_key(input, expected):
1749 assert _match_number_in_dict_key_prefix(input) == expected
1757 assert _match_number_in_dict_key_prefix(input) == expected
@@ -1,582 +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 @pytest.mark.xfail(
58 sys.version_info.releaselevel not in ("final", "candidate"),
59 reason="fails on 3.13.dev",
60 )
61 def test_ipdb_magics():
57 def test_ipdb_magics():
62 '''Test calling some IPython magics from ipdb.
58 '''Test calling some IPython magics from ipdb.
63
59
64 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.
65
61
66 >>> class ExampleClass(object):
62 >>> class ExampleClass(object):
67 ... """Docstring for ExampleClass."""
63 ... """Docstring for ExampleClass."""
68 ... def __init__(self):
64 ... def __init__(self):
69 ... """Docstring for ExampleClass.__init__"""
65 ... """Docstring for ExampleClass.__init__"""
70 ... pass
66 ... pass
71 ... def __str__(self):
67 ... def __str__(self):
72 ... return "ExampleClass()"
68 ... return "ExampleClass()"
73
69
74 >>> def example_function(x, y, z="hello"):
70 >>> def example_function(x, y, z="hello"):
75 ... """Docstring for example_function."""
71 ... """Docstring for example_function."""
76 ... pass
72 ... pass
77
73
78 >>> old_trace = sys.gettrace()
74 >>> old_trace = sys.gettrace()
79
75
80 Create a function which triggers ipdb.
76 Create a function which triggers ipdb.
81
77
82 >>> def trigger_ipdb():
78 >>> def trigger_ipdb():
83 ... a = ExampleClass()
79 ... a = ExampleClass()
84 ... debugger.Pdb().set_trace()
80 ... debugger.Pdb().set_trace()
85
81
86 >>> with PdbTestInput([
82 >>> with PdbTestInput([
87 ... 'pdef example_function',
83 ... 'pdef example_function',
88 ... 'pdoc ExampleClass',
84 ... 'pdoc ExampleClass',
89 ... 'up',
85 ... 'up',
90 ... 'down',
86 ... 'down',
91 ... 'list',
87 ... 'list',
92 ... 'pinfo a',
88 ... 'pinfo a',
93 ... 'll',
89 ... 'll',
94 ... 'continue',
90 ... 'continue',
95 ... ]):
91 ... ]):
96 ... trigger_ipdb()
92 ... trigger_ipdb()
97 --Return--
93 --Return--
98 None
94 None
99 > <doctest ...>(3)trigger_ipdb()
95 > <doctest ...>(3)trigger_ipdb()
100 1 def trigger_ipdb():
96 1 def trigger_ipdb():
101 2 a = ExampleClass()
97 2 a = ExampleClass()
102 ----> 3 debugger.Pdb().set_trace()
98 ----> 3 debugger.Pdb().set_trace()
103 <BLANKLINE>
99 <BLANKLINE>
104 ipdb> pdef example_function
100 ipdb> pdef example_function
105 example_function(x, y, z='hello')
101 example_function(x, y, z='hello')
106 ipdb> pdoc ExampleClass
102 ipdb> pdoc ExampleClass
107 Class docstring:
103 Class docstring:
108 Docstring for ExampleClass.
104 Docstring for ExampleClass.
109 Init docstring:
105 Init docstring:
110 Docstring for ExampleClass.__init__
106 Docstring for ExampleClass.__init__
111 ipdb> up
107 ipdb> up
112 > <doctest ...>(11)<module>()
108 > <doctest ...>(11)<module>()
113 7 'pinfo a',
109 7 'pinfo a',
114 8 'll',
110 8 'll',
115 9 'continue',
111 9 'continue',
116 10 ]):
112 10 ]):
117 ---> 11 trigger_ipdb()
113 ---> 11 trigger_ipdb()
118 <BLANKLINE>
114 <BLANKLINE>
119 ipdb> down
115 ipdb> down
120 None
116 None
121 > <doctest ...>(3)trigger_ipdb()
117 > <doctest ...>(3)trigger_ipdb()
122 1 def trigger_ipdb():
118 1 def trigger_ipdb():
123 2 a = ExampleClass()
119 2 a = ExampleClass()
124 ----> 3 debugger.Pdb().set_trace()
120 ----> 3 debugger.Pdb().set_trace()
125 <BLANKLINE>
121 <BLANKLINE>
126 ipdb> list
122 ipdb> list
127 1 def trigger_ipdb():
123 1 def trigger_ipdb():
128 2 a = ExampleClass()
124 2 a = ExampleClass()
129 ----> 3 debugger.Pdb().set_trace()
125 ----> 3 debugger.Pdb().set_trace()
130 <BLANKLINE>
126 <BLANKLINE>
131 ipdb> pinfo a
127 ipdb> pinfo a
132 Type: ExampleClass
128 Type: ExampleClass
133 String form: ExampleClass()
129 String form: ExampleClass()
134 Namespace: Local...
130 Namespace: Local...
135 Docstring: Docstring for ExampleClass.
131 Docstring: Docstring for ExampleClass.
136 Init docstring: Docstring for ExampleClass.__init__
132 Init docstring: Docstring for ExampleClass.__init__
137 ipdb> ll
133 ipdb> ll
138 1 def trigger_ipdb():
134 1 def trigger_ipdb():
139 2 a = ExampleClass()
135 2 a = ExampleClass()
140 ----> 3 debugger.Pdb().set_trace()
136 ----> 3 debugger.Pdb().set_trace()
141 <BLANKLINE>
137 <BLANKLINE>
142 ipdb> continue
138 ipdb> continue
143
139
144 Restore previous trace function, e.g. for coverage.py
140 Restore previous trace function, e.g. for coverage.py
145
141
146 >>> sys.settrace(old_trace)
142 >>> sys.settrace(old_trace)
147 '''
143 '''
148
144
149 def test_ipdb_magics2():
145 def test_ipdb_magics2():
150 '''Test ipdb with a very short function.
146 '''Test ipdb with a very short function.
151
147
152 >>> old_trace = sys.gettrace()
148 >>> old_trace = sys.gettrace()
153
149
154 >>> def bar():
150 >>> def bar():
155 ... pass
151 ... pass
156
152
157 Run ipdb.
153 Run ipdb.
158
154
159 >>> with PdbTestInput([
155 >>> with PdbTestInput([
160 ... 'continue',
156 ... 'continue',
161 ... ]):
157 ... ]):
162 ... debugger.Pdb().runcall(bar)
158 ... debugger.Pdb().runcall(bar)
163 > <doctest ...>(2)bar()
159 > <doctest ...>(2)bar()
164 1 def bar():
160 1 def bar():
165 ----> 2 pass
161 ----> 2 pass
166 <BLANKLINE>
162 <BLANKLINE>
167 ipdb> continue
163 ipdb> continue
168
164
169 Restore previous trace function, e.g. for coverage.py
165 Restore previous trace function, e.g. for coverage.py
170
166
171 >>> sys.settrace(old_trace)
167 >>> sys.settrace(old_trace)
172 '''
168 '''
173
169
174 def can_quit():
170 def can_quit():
175 '''Test that quit work in ipydb
171 '''Test that quit work in ipydb
176
172
177 >>> old_trace = sys.gettrace()
173 >>> old_trace = sys.gettrace()
178
174
179 >>> def bar():
175 >>> def bar():
180 ... pass
176 ... pass
181
177
182 >>> with PdbTestInput([
178 >>> with PdbTestInput([
183 ... 'quit',
179 ... 'quit',
184 ... ]):
180 ... ]):
185 ... debugger.Pdb().runcall(bar)
181 ... debugger.Pdb().runcall(bar)
186 > <doctest ...>(2)bar()
182 > <doctest ...>(2)bar()
187 1 def bar():
183 1 def bar():
188 ----> 2 pass
184 ----> 2 pass
189 <BLANKLINE>
185 <BLANKLINE>
190 ipdb> quit
186 ipdb> quit
191
187
192 Restore previous trace function, e.g. for coverage.py
188 Restore previous trace function, e.g. for coverage.py
193
189
194 >>> sys.settrace(old_trace)
190 >>> sys.settrace(old_trace)
195 '''
191 '''
196
192
197
193
198 def can_exit():
194 def can_exit():
199 '''Test that quit work in ipydb
195 '''Test that quit work in ipydb
200
196
201 >>> old_trace = sys.gettrace()
197 >>> old_trace = sys.gettrace()
202
198
203 >>> def bar():
199 >>> def bar():
204 ... pass
200 ... pass
205
201
206 >>> with PdbTestInput([
202 >>> with PdbTestInput([
207 ... 'exit',
203 ... 'exit',
208 ... ]):
204 ... ]):
209 ... debugger.Pdb().runcall(bar)
205 ... debugger.Pdb().runcall(bar)
210 > <doctest ...>(2)bar()
206 > <doctest ...>(2)bar()
211 1 def bar():
207 1 def bar():
212 ----> 2 pass
208 ----> 2 pass
213 <BLANKLINE>
209 <BLANKLINE>
214 ipdb> exit
210 ipdb> exit
215
211
216 Restore previous trace function, e.g. for coverage.py
212 Restore previous trace function, e.g. for coverage.py
217
213
218 >>> sys.settrace(old_trace)
214 >>> sys.settrace(old_trace)
219 '''
215 '''
220
216
221
217
222 def test_interruptible_core_debugger():
218 def test_interruptible_core_debugger():
223 """The debugger can be interrupted.
219 """The debugger can be interrupted.
224
220
225 The presumption is there is some mechanism that causes a KeyboardInterrupt
221 The presumption is there is some mechanism that causes a KeyboardInterrupt
226 (this is implemented in ipykernel). We want to ensure the
222 (this is implemented in ipykernel). We want to ensure the
227 KeyboardInterrupt cause debugging to cease.
223 KeyboardInterrupt cause debugging to cease.
228 """
224 """
229 def raising_input(msg="", called=[0]):
225 def raising_input(msg="", called=[0]):
230 called[0] += 1
226 called[0] += 1
231 assert called[0] == 1, "input() should only be called once!"
227 assert called[0] == 1, "input() should only be called once!"
232 raise KeyboardInterrupt()
228 raise KeyboardInterrupt()
233
229
234 tracer_orig = sys.gettrace()
230 tracer_orig = sys.gettrace()
235 try:
231 try:
236 with patch.object(builtins, "input", raising_input):
232 with patch.object(builtins, "input", raising_input):
237 debugger.InterruptiblePdb().set_trace()
233 debugger.InterruptiblePdb().set_trace()
238 # 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,
239 # resulting in a timeout by the test runner. The alternative
235 # resulting in a timeout by the test runner. The alternative
240 # implementation would involve a subprocess, but that adds issues
236 # implementation would involve a subprocess, but that adds issues
241 # with interrupting subprocesses that are rather complex, so it's
237 # with interrupting subprocesses that are rather complex, so it's
242 # simpler just to do it this way.
238 # simpler just to do it this way.
243 finally:
239 finally:
244 # restore the original trace function
240 # restore the original trace function
245 sys.settrace(tracer_orig)
241 sys.settrace(tracer_orig)
246
242
247
243
248 @skip_win32
244 @skip_win32
249 def test_xmode_skip():
245 def test_xmode_skip():
250 """that xmode skip frames
246 """that xmode skip frames
251
247
252 Not as a doctest as pytest does not run doctests.
248 Not as a doctest as pytest does not run doctests.
253 """
249 """
254 import pexpect
250 import pexpect
255 env = os.environ.copy()
251 env = os.environ.copy()
256 env["IPY_TEST_SIMPLE_PROMPT"] = "1"
252 env["IPY_TEST_SIMPLE_PROMPT"] = "1"
257
253
258 child = pexpect.spawn(
254 child = pexpect.spawn(
259 sys.executable, ["-m", "IPython", "--colors=nocolor"], env=env
255 sys.executable, ["-m", "IPython", "--colors=nocolor"], env=env
260 )
256 )
261 child.timeout = 15 * IPYTHON_TESTING_TIMEOUT_SCALE
257 child.timeout = 15 * IPYTHON_TESTING_TIMEOUT_SCALE
262
258
263 child.expect("IPython")
259 child.expect("IPython")
264 child.expect("\n")
260 child.expect("\n")
265 child.expect_exact("In [1]")
261 child.expect_exact("In [1]")
266
262
267 block = dedent(
263 block = dedent(
268 """
264 """
269 def f():
265 def f():
270 __tracebackhide__ = True
266 __tracebackhide__ = True
271 g()
267 g()
272
268
273 def g():
269 def g():
274 raise ValueError
270 raise ValueError
275
271
276 f()
272 f()
277 """
273 """
278 )
274 )
279
275
280 for line in block.splitlines():
276 for line in block.splitlines():
281 child.sendline(line)
277 child.sendline(line)
282 child.expect_exact(line)
278 child.expect_exact(line)
283 child.expect_exact("skipping")
279 child.expect_exact("skipping")
284
280
285 block = dedent(
281 block = dedent(
286 """
282 """
287 def f():
283 def f():
288 __tracebackhide__ = True
284 __tracebackhide__ = True
289 g()
285 g()
290
286
291 def g():
287 def g():
292 from IPython.core.debugger import set_trace
288 from IPython.core.debugger import set_trace
293 set_trace()
289 set_trace()
294
290
295 f()
291 f()
296 """
292 """
297 )
293 )
298
294
299 for line in block.splitlines():
295 for line in block.splitlines():
300 child.sendline(line)
296 child.sendline(line)
301 child.expect_exact(line)
297 child.expect_exact(line)
302
298
303 child.expect("ipdb>")
299 child.expect("ipdb>")
304 child.sendline("w")
300 child.sendline("w")
305 child.expect("hidden")
301 child.expect("hidden")
306 child.expect("ipdb>")
302 child.expect("ipdb>")
307 child.sendline("skip_hidden false")
303 child.sendline("skip_hidden false")
308 child.sendline("w")
304 child.sendline("w")
309 child.expect("__traceba")
305 child.expect("__traceba")
310 child.expect("ipdb>")
306 child.expect("ipdb>")
311
307
312 child.close()
308 child.close()
313
309
314
310
315 skip_decorators_blocks = (
311 skip_decorators_blocks = (
316 """
312 """
317 def helpers_helper():
313 def helpers_helper():
318 pass # should not stop here except breakpoint
314 pass # should not stop here except breakpoint
319 """,
315 """,
320 """
316 """
321 def helper_1():
317 def helper_1():
322 helpers_helper() # should not stop here
318 helpers_helper() # should not stop here
323 """,
319 """,
324 """
320 """
325 def helper_2():
321 def helper_2():
326 pass # should not stop here
322 pass # should not stop here
327 """,
323 """,
328 """
324 """
329 def pdb_skipped_decorator2(function):
325 def pdb_skipped_decorator2(function):
330 def wrapped_fn(*args, **kwargs):
326 def wrapped_fn(*args, **kwargs):
331 __debuggerskip__ = True
327 __debuggerskip__ = True
332 helper_2()
328 helper_2()
333 __debuggerskip__ = False
329 __debuggerskip__ = False
334 result = function(*args, **kwargs)
330 result = function(*args, **kwargs)
335 __debuggerskip__ = True
331 __debuggerskip__ = True
336 helper_2()
332 helper_2()
337 return result
333 return result
338 return wrapped_fn
334 return wrapped_fn
339 """,
335 """,
340 """
336 """
341 def pdb_skipped_decorator(function):
337 def pdb_skipped_decorator(function):
342 def wrapped_fn(*args, **kwargs):
338 def wrapped_fn(*args, **kwargs):
343 __debuggerskip__ = True
339 __debuggerskip__ = True
344 helper_1()
340 helper_1()
345 __debuggerskip__ = False
341 __debuggerskip__ = False
346 result = function(*args, **kwargs)
342 result = function(*args, **kwargs)
347 __debuggerskip__ = True
343 __debuggerskip__ = True
348 helper_2()
344 helper_2()
349 return result
345 return result
350 return wrapped_fn
346 return wrapped_fn
351 """,
347 """,
352 """
348 """
353 @pdb_skipped_decorator
349 @pdb_skipped_decorator
354 @pdb_skipped_decorator2
350 @pdb_skipped_decorator2
355 def bar(x, y):
351 def bar(x, y):
356 return x * y
352 return x * y
357 """,
353 """,
358 """import IPython.terminal.debugger as ipdb""",
354 """import IPython.terminal.debugger as ipdb""",
359 """
355 """
360 def f():
356 def f():
361 ipdb.set_trace()
357 ipdb.set_trace()
362 bar(3, 4)
358 bar(3, 4)
363 """,
359 """,
364 """
360 """
365 f()
361 f()
366 """,
362 """,
367 )
363 )
368
364
369
365
370 def _decorator_skip_setup():
366 def _decorator_skip_setup():
371 import pexpect
367 import pexpect
372
368
373 env = os.environ.copy()
369 env = os.environ.copy()
374 env["IPY_TEST_SIMPLE_PROMPT"] = "1"
370 env["IPY_TEST_SIMPLE_PROMPT"] = "1"
375 env["PROMPT_TOOLKIT_NO_CPR"] = "1"
371 env["PROMPT_TOOLKIT_NO_CPR"] = "1"
376
372
377 child = pexpect.spawn(
373 child = pexpect.spawn(
378 sys.executable, ["-m", "IPython", "--colors=nocolor"], env=env
374 sys.executable, ["-m", "IPython", "--colors=nocolor"], env=env
379 )
375 )
380 child.timeout = 15 * IPYTHON_TESTING_TIMEOUT_SCALE
376 child.timeout = 15 * IPYTHON_TESTING_TIMEOUT_SCALE
381
377
382 child.expect("IPython")
378 child.expect("IPython")
383 child.expect("\n")
379 child.expect("\n")
384
380
385 child.timeout = 5 * IPYTHON_TESTING_TIMEOUT_SCALE
381 child.timeout = 5 * IPYTHON_TESTING_TIMEOUT_SCALE
386 child.str_last_chars = 500
382 child.str_last_chars = 500
387
383
388 dedented_blocks = [dedent(b).strip() for b in skip_decorators_blocks]
384 dedented_blocks = [dedent(b).strip() for b in skip_decorators_blocks]
389 in_prompt_number = 1
385 in_prompt_number = 1
390 for cblock in dedented_blocks:
386 for cblock in dedented_blocks:
391 child.expect_exact(f"In [{in_prompt_number}]:")
387 child.expect_exact(f"In [{in_prompt_number}]:")
392 in_prompt_number += 1
388 in_prompt_number += 1
393 for line in cblock.splitlines():
389 for line in cblock.splitlines():
394 child.sendline(line)
390 child.sendline(line)
395 child.expect_exact(line)
391 child.expect_exact(line)
396 child.sendline("")
392 child.sendline("")
397 return child
393 return child
398
394
399
395
400 @pytest.mark.skip(reason="recently fail for unknown reason on CI")
396 @pytest.mark.skip(reason="recently fail for unknown reason on CI")
401 @skip_win32
397 @skip_win32
402 def test_decorator_skip():
398 def test_decorator_skip():
403 """test that decorator frames can be skipped."""
399 """test that decorator frames can be skipped."""
404
400
405 child = _decorator_skip_setup()
401 child = _decorator_skip_setup()
406
402
407 child.expect_exact("ipython-input-8")
403 child.expect_exact("ipython-input-8")
408 child.expect_exact("3 bar(3, 4)")
404 child.expect_exact("3 bar(3, 4)")
409 child.expect("ipdb>")
405 child.expect("ipdb>")
410
406
411 child.expect("ipdb>")
407 child.expect("ipdb>")
412 child.sendline("step")
408 child.sendline("step")
413 child.expect_exact("step")
409 child.expect_exact("step")
414 child.expect_exact("--Call--")
410 child.expect_exact("--Call--")
415 child.expect_exact("ipython-input-6")
411 child.expect_exact("ipython-input-6")
416
412
417 child.expect_exact("1 @pdb_skipped_decorator")
413 child.expect_exact("1 @pdb_skipped_decorator")
418
414
419 child.sendline("s")
415 child.sendline("s")
420 child.expect_exact("return x * y")
416 child.expect_exact("return x * y")
421
417
422 child.close()
418 child.close()
423
419
424
420
425 @pytest.mark.skip(reason="recently fail for unknown reason on CI")
421 @pytest.mark.skip(reason="recently fail for unknown reason on CI")
426 @pytest.mark.skipif(platform.python_implementation() == "PyPy", reason="issues on PyPy")
422 @pytest.mark.skipif(platform.python_implementation() == "PyPy", reason="issues on PyPy")
427 @skip_win32
423 @skip_win32
428 def test_decorator_skip_disabled():
424 def test_decorator_skip_disabled():
429 """test that decorator frame skipping can be disabled"""
425 """test that decorator frame skipping can be disabled"""
430
426
431 child = _decorator_skip_setup()
427 child = _decorator_skip_setup()
432
428
433 child.expect_exact("3 bar(3, 4)")
429 child.expect_exact("3 bar(3, 4)")
434
430
435 for input_, expected in [
431 for input_, expected in [
436 ("skip_predicates debuggerskip False", ""),
432 ("skip_predicates debuggerskip False", ""),
437 ("skip_predicates", "debuggerskip : False"),
433 ("skip_predicates", "debuggerskip : False"),
438 ("step", "---> 2 def wrapped_fn"),
434 ("step", "---> 2 def wrapped_fn"),
439 ("step", "----> 3 __debuggerskip__"),
435 ("step", "----> 3 __debuggerskip__"),
440 ("step", "----> 4 helper_1()"),
436 ("step", "----> 4 helper_1()"),
441 ("step", "---> 1 def helper_1():"),
437 ("step", "---> 1 def helper_1():"),
442 ("next", "----> 2 helpers_helper()"),
438 ("next", "----> 2 helpers_helper()"),
443 ("next", "--Return--"),
439 ("next", "--Return--"),
444 ("next", "----> 5 __debuggerskip__ = False"),
440 ("next", "----> 5 __debuggerskip__ = False"),
445 ]:
441 ]:
446 child.expect("ipdb>")
442 child.expect("ipdb>")
447 child.sendline(input_)
443 child.sendline(input_)
448 child.expect_exact(input_)
444 child.expect_exact(input_)
449 child.expect_exact(expected)
445 child.expect_exact(expected)
450
446
451 child.close()
447 child.close()
452
448
453
449
454 @pytest.mark.xfail(
450 @pytest.mark.xfail(
455 sys.version_info.releaselevel not in ("final", "candidate"),
451 sys.version_info.releaselevel not in ("final", "candidate"),
456 reason="fails on 3.13.dev",
452 reason="fails on 3.13.dev",
453 strict=True,
457 )
454 )
458 @pytest.mark.skipif(platform.python_implementation() == "PyPy", reason="issues on PyPy")
455 @pytest.mark.skipif(platform.python_implementation() == "PyPy", reason="issues on PyPy")
459 @skip_win32
456 @skip_win32
460 def test_decorator_skip_with_breakpoint():
457 def test_decorator_skip_with_breakpoint():
461 """test that decorator frame skipping can be disabled"""
458 """test that decorator frame skipping can be disabled"""
462
459
463 import pexpect
460 import pexpect
464
461
465 env = os.environ.copy()
462 env = os.environ.copy()
466 env["IPY_TEST_SIMPLE_PROMPT"] = "1"
463 env["IPY_TEST_SIMPLE_PROMPT"] = "1"
467 env["PROMPT_TOOLKIT_NO_CPR"] = "1"
464 env["PROMPT_TOOLKIT_NO_CPR"] = "1"
468
465
469 child = pexpect.spawn(
466 child = pexpect.spawn(
470 sys.executable, ["-m", "IPython", "--colors=nocolor"], env=env
467 sys.executable, ["-m", "IPython", "--colors=nocolor"], env=env
471 )
468 )
472 child.timeout = 15 * IPYTHON_TESTING_TIMEOUT_SCALE
469 child.timeout = 15 * IPYTHON_TESTING_TIMEOUT_SCALE
473 child.str_last_chars = 500
470 child.str_last_chars = 500
474
471
475 child.expect("IPython")
472 child.expect("IPython")
476 child.expect("\n")
473 child.expect("\n")
477
474
478 child.timeout = 5 * IPYTHON_TESTING_TIMEOUT_SCALE
475 child.timeout = 5 * IPYTHON_TESTING_TIMEOUT_SCALE
479
476
480 ### 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
481 with NamedTemporaryFile(suffix=".py", dir=".", delete=True) as tf:
478 with NamedTemporaryFile(suffix=".py", dir=".", delete=True) as tf:
482 name = tf.name[:-3].split("/")[-1]
479 name = tf.name[:-3].split("/")[-1]
483 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())
484 tf.flush()
481 tf.flush()
485 codeblock = f"from {name} import f"
482 codeblock = f"from {name} import f"
486
483
487 dedented_blocks = [
484 dedented_blocks = [
488 codeblock,
485 codeblock,
489 "f()",
486 "f()",
490 ]
487 ]
491
488
492 in_prompt_number = 1
489 in_prompt_number = 1
493 for cblock in dedented_blocks:
490 for cblock in dedented_blocks:
494 child.expect_exact(f"In [{in_prompt_number}]:")
491 child.expect_exact(f"In [{in_prompt_number}]:")
495 in_prompt_number += 1
492 in_prompt_number += 1
496 for line in cblock.splitlines():
493 for line in cblock.splitlines():
497 child.sendline(line)
494 child.sendline(line)
498 child.expect_exact(line)
495 child.expect_exact(line)
499 child.sendline("")
496 child.sendline("")
500
497
501 # 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
502 child.expect_exact("47 bar(3, 4)")
499 child.expect_exact("47 bar(3, 4)")
503
500
504 for input_, expected in [
501 for input_, expected in [
505 (f"b {name}.py:3", ""),
502 (f"b {name}.py:3", ""),
506 ("step", "1---> 3 pass # should not stop here except"),
503 ("step", "1---> 3 pass # should not stop here except"),
507 ("step", "---> 38 @pdb_skipped_decorator"),
504 ("step", "---> 38 @pdb_skipped_decorator"),
508 ("continue", ""),
505 ("continue", ""),
509 ]:
506 ]:
510 child.expect("ipdb>")
507 child.expect("ipdb>")
511 child.sendline(input_)
508 child.sendline(input_)
512 child.expect_exact(input_)
509 child.expect_exact(input_)
513 child.expect_exact(expected)
510 child.expect_exact(expected)
514
511
515 child.close()
512 child.close()
516
513
517
514
518 @skip_win32
515 @skip_win32
519 def test_where_erase_value():
516 def test_where_erase_value():
520 """Test that `where` does not access f_locals and erase values."""
517 """Test that `where` does not access f_locals and erase values."""
521 import pexpect
518 import pexpect
522
519
523 env = os.environ.copy()
520 env = os.environ.copy()
524 env["IPY_TEST_SIMPLE_PROMPT"] = "1"
521 env["IPY_TEST_SIMPLE_PROMPT"] = "1"
525
522
526 child = pexpect.spawn(
523 child = pexpect.spawn(
527 sys.executable, ["-m", "IPython", "--colors=nocolor"], env=env
524 sys.executable, ["-m", "IPython", "--colors=nocolor"], env=env
528 )
525 )
529 child.timeout = 15 * IPYTHON_TESTING_TIMEOUT_SCALE
526 child.timeout = 15 * IPYTHON_TESTING_TIMEOUT_SCALE
530
527
531 child.expect("IPython")
528 child.expect("IPython")
532 child.expect("\n")
529 child.expect("\n")
533 child.expect_exact("In [1]")
530 child.expect_exact("In [1]")
534
531
535 block = dedent(
532 block = dedent(
536 """
533 """
537 def simple_f():
534 def simple_f():
538 myvar = 1
535 myvar = 1
539 print(myvar)
536 print(myvar)
540 1/0
537 1/0
541 print(myvar)
538 print(myvar)
542 simple_f() """
539 simple_f() """
543 )
540 )
544
541
545 for line in block.splitlines():
542 for line in block.splitlines():
546 child.sendline(line)
543 child.sendline(line)
547 child.expect_exact(line)
544 child.expect_exact(line)
548 child.expect_exact("ZeroDivisionError")
545 child.expect_exact("ZeroDivisionError")
549 child.expect_exact("In [2]:")
546 child.expect_exact("In [2]:")
550
547
551 child.sendline("%debug")
548 child.sendline("%debug")
552
549
553 ##
550 ##
554 child.expect("ipdb>")
551 child.expect("ipdb>")
555
552
556 child.sendline("myvar")
553 child.sendline("myvar")
557 child.expect("1")
554 child.expect("1")
558
555
559 ##
556 ##
560 child.expect("ipdb>")
557 child.expect("ipdb>")
561
558
562 child.sendline("myvar = 2")
559 child.sendline("myvar = 2")
563
560
564 ##
561 ##
565 child.expect_exact("ipdb>")
562 child.expect_exact("ipdb>")
566
563
567 child.sendline("myvar")
564 child.sendline("myvar")
568
565
569 child.expect_exact("2")
566 child.expect_exact("2")
570
567
571 ##
568 ##
572 child.expect("ipdb>")
569 child.expect("ipdb>")
573 child.sendline("where")
570 child.sendline("where")
574
571
575 ##
572 ##
576 child.expect("ipdb>")
573 child.expect("ipdb>")
577 child.sendline("myvar")
574 child.sendline("myvar")
578
575
579 child.expect_exact("2")
576 child.expect_exact("2")
580 child.expect("ipdb>")
577 child.expect("ipdb>")
581
578
582 child.close()
579 child.close()
@@ -1,588 +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 @pytest.mark.xfail(
491 @pytest.mark.xfail(
492 sys.version_info.releaselevel not in ("final", "candidate"),
492 sys.version_info.releaselevel not in ("final", "candidate"),
493 reason="fails on 3.13.dev",
493 reason="fails on 3.13.dev",
494 strict=True,
494 )
495 )
495 def test_pinfo_docstring_dynamic(capsys):
496 def test_pinfo_docstring_dynamic(capsys):
496 obj_def = """class Bar:
497 obj_def = """class Bar:
497 __custom_documentations__ = {
498 __custom_documentations__ = {
498 "prop" : "cdoc for prop",
499 "prop" : "cdoc for prop",
499 "non_exist" : "cdoc for non_exist",
500 "non_exist" : "cdoc for non_exist",
500 }
501 }
501 @property
502 @property
502 def prop(self):
503 def prop(self):
503 '''
504 '''
504 Docstring for prop
505 Docstring for prop
505 '''
506 '''
506 return self._prop
507 return self._prop
507
508
508 @prop.setter
509 @prop.setter
509 def prop(self, v):
510 def prop(self, v):
510 self._prop = v
511 self._prop = v
511 """
512 """
512 ip.run_cell(obj_def)
513 ip.run_cell(obj_def)
513
514
514 ip.run_cell("b = Bar()")
515 ip.run_cell("b = Bar()")
515
516
516 ip.run_line_magic("pinfo", "b.prop")
517 ip.run_line_magic("pinfo", "b.prop")
517 captured = capsys.readouterr()
518 captured = capsys.readouterr()
518 assert "Docstring: cdoc for prop" in captured.out
519 assert "Docstring: cdoc for prop" in captured.out
519
520
520 ip.run_line_magic("pinfo", "b.non_exist")
521 ip.run_line_magic("pinfo", "b.non_exist")
521 captured = capsys.readouterr()
522 captured = capsys.readouterr()
522 assert "Docstring: cdoc for non_exist" in captured.out
523 assert "Docstring: cdoc for non_exist" in captured.out
523
524
524 ip.run_cell("b.prop?")
525 ip.run_cell("b.prop?")
525 captured = capsys.readouterr()
526 captured = capsys.readouterr()
526 assert "Docstring: cdoc for prop" in captured.out
527 assert "Docstring: cdoc for prop" in captured.out
527
528
528 ip.run_cell("b.non_exist?")
529 ip.run_cell("b.non_exist?")
529 captured = capsys.readouterr()
530 captured = capsys.readouterr()
530 assert "Docstring: cdoc for non_exist" in captured.out
531 assert "Docstring: cdoc for non_exist" in captured.out
531
532
532 ip.run_cell("b.undefined?")
533 ip.run_cell("b.undefined?")
533 captured = capsys.readouterr()
534 captured = capsys.readouterr()
534 assert "Docstring: <no docstring>" in captured.out
535 assert "Docstring: <no docstring>" in captured.out
535
536
536
537
537 def test_pinfo_magic():
538 def test_pinfo_magic():
538 with AssertPrints("Docstring:"):
539 with AssertPrints("Docstring:"):
539 ip._inspect("pinfo", "lsmagic", detail_level=0)
540 ip._inspect("pinfo", "lsmagic", detail_level=0)
540
541
541 with AssertPrints("Source:"):
542 with AssertPrints("Source:"):
542 ip._inspect("pinfo", "lsmagic", detail_level=1)
543 ip._inspect("pinfo", "lsmagic", detail_level=1)
543
544
544
545
545 def test_init_colors():
546 def test_init_colors():
546 # ensure colors are not present in signature info
547 # ensure colors are not present in signature info
547 info = inspector.info(HasSignature)
548 info = inspector.info(HasSignature)
548 init_def = info["init_definition"]
549 init_def = info["init_definition"]
549 assert "[0m" not in init_def
550 assert "[0m" not in init_def
550
551
551
552
552 def test_builtin_init():
553 def test_builtin_init():
553 info = inspector.info(list)
554 info = inspector.info(list)
554 init_def = info['init_definition']
555 init_def = info['init_definition']
555 assert init_def is not None
556 assert init_def is not None
556
557
557
558
558 def test_render_signature_short():
559 def test_render_signature_short():
559 def short_fun(a=1): pass
560 def short_fun(a=1): pass
560 sig = oinspect._render_signature(
561 sig = oinspect._render_signature(
561 signature(short_fun),
562 signature(short_fun),
562 short_fun.__name__,
563 short_fun.__name__,
563 )
564 )
564 assert sig == "short_fun(a=1)"
565 assert sig == "short_fun(a=1)"
565
566
566
567
567 def test_render_signature_long():
568 def test_render_signature_long():
568 from typing import Optional
569 from typing import Optional
569
570
570 def long_function(
571 def long_function(
571 a_really_long_parameter: int,
572 a_really_long_parameter: int,
572 and_another_long_one: bool = False,
573 and_another_long_one: bool = False,
573 let_us_make_sure_this_is_looong: Optional[str] = None,
574 let_us_make_sure_this_is_looong: Optional[str] = None,
574 ) -> bool: pass
575 ) -> bool: pass
575
576
576 sig = oinspect._render_signature(
577 sig = oinspect._render_signature(
577 signature(long_function),
578 signature(long_function),
578 long_function.__name__,
579 long_function.__name__,
579 )
580 )
580 expected = """\
581 expected = """\
581 long_function(
582 long_function(
582 a_really_long_parameter: int,
583 a_really_long_parameter: int,
583 and_another_long_one: bool = False,
584 and_another_long_one: bool = False,
584 let_us_make_sure_this_is_looong: Optional[str] = None,
585 let_us_make_sure_this_is_looong: Optional[str] = None,
585 ) -> bool\
586 ) -> bool\
586 """
587 """
587
588
588 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