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