1 # encoding: utf-8 2 """Tests for the IPython tab-completion machinery.""" 3 4 # Copyright (c) IPython Development Team. 5 # Distributed under the terms of the Modified BSD License. 6 7 import os 8 import pytest 9 import sys 10 import textwrap 11 import unittest 12 import random 13 14 from importlib.metadata import version 15 16 from contextlib import contextmanager 17 18 from traitlets.config.loader import Config 19 from IPython import get_ipython 20 from IPython.core import completer 21 from IPython.utils.tempdir import TemporaryDirectory, TemporaryWorkingDirectory 22 from IPython.utils.generics import complete_object 23 from IPython.testing import decorators as dec 24 from IPython.core.latex_symbols import latex_symbols 25 26 from IPython.core.completer import ( 27 Completion, 28 provisionalcompleter, 29 match_dict_keys, 30 _deduplicate_completions, 31 _match_number_in_dict_key_prefix, 32 completion_matcher, 33 SimpleCompletion, 34 CompletionContext, 35 _unicode_name_compute, 36 _UNICODE_RANGES, 37 ) 38 39 from packaging.version import parse 40 41 42 @contextmanager 43 def jedi_status(status: bool): 44 completer = get_ipython().Completer 45 try: 46 old = completer.use_jedi 47 completer.use_jedi = status 48 yield 49 finally: 50 completer.use_jedi = old 51 52 53 # ----------------------------------------------------------------------------- 54 # Test functions 55 # ----------------------------------------------------------------------------- 56 57 58 def recompute_unicode_ranges(): 59 """ 60 utility to recompute the largest unicode range without any characters 61 62 use to recompute the gap in the global _UNICODE_RANGES of completer.py 63 """ 64 import itertools 65 import unicodedata 66 67 valid = [] 68 for c in range(0, 0x10FFFF + 1): 69 try: 70 unicodedata.name(chr(c)) 71 except ValueError: 72 continue 73 valid.append(c) 74 75 def ranges(i): 76 for a, b in itertools.groupby(enumerate(i), lambda pair: pair[1] - pair[0]): 77 b = list(b) 78 yield b[0][1], b[-1][1] 79 80 rg = list(ranges(valid)) 81 lens = [] 82 gap_lens = [] 83 _pstart, pstop = 0, 0 84 for start, stop in rg: 85 lens.append(stop - start) 86 gap_lens.append( 87 ( 88 start - pstop, 89 hex(pstop + 1), 90 hex(start), 91 f"{round((start - pstop)/0xe01f0*100)}%", 92 ) 93 ) 94 _pstart, pstop = start, stop 95 96 return sorted(gap_lens)[-1] 97 98 99 def test_unicode_range(): 100 """ 101 Test that the ranges we test for unicode names give the same number of 102 results than testing the full length. 103 """ 104 105 expected_list = _unicode_name_compute([(0, 0x110000)]) 106 test = _unicode_name_compute(_UNICODE_RANGES) 107 len_exp = len(expected_list) 108 len_test = len(test) 109 110 # do not inline the len() or on error pytest will try to print the 130 000 + 111 # elements. 112 message = None 113 if len_exp != len_test or len_exp > 131808: 114 size, start, stop, prct = recompute_unicode_ranges() 115 message = f"""_UNICODE_RANGES likely wrong and need updating. This is 116 likely due to a new release of Python. We've find that the biggest gap 117 in unicode characters has reduces in size to be {size} characters 118 ({prct}), from {start}, to {stop}. In completer.py likely update to 119 120 _UNICODE_RANGES = [(32, {start}), ({stop}, 0xe01f0)] 121 122 And update the assertion below to use 123 124 len_exp <= {len_exp} 125 """ 126 assert len_exp == len_test, message 127 128 # fail if new unicode symbols have been added. 129 assert len_exp <= 143668, message 130 131 132 @contextmanager 133 def greedy_completion(): 134 ip = get_ipython() 135 greedy_original = ip.Completer.greedy 136 try: 137 ip.Completer.greedy = True 138 yield 139 finally: 140 ip.Completer.greedy = greedy_original 141 142 143 @contextmanager 144 def evaluation_policy(evaluation: str): 145 ip = get_ipython() 146 evaluation_original = ip.Completer.evaluation 147 try: 148 ip.Completer.evaluation = evaluation 149 yield 150 finally: 151 ip.Completer.evaluation = evaluation_original 152 153 154 @contextmanager 155 def custom_matchers(matchers): 156 ip = get_ipython() 157 try: 158 ip.Completer.custom_matchers.extend(matchers) 159 yield 160 finally: 161 ip.Completer.custom_matchers.clear() 162 163 164 if sys.platform == "win32": 165 pairs = [ 166 ("abc", "abc"), 167 (" abc", '" abc"'), 168 ("a bc", '"a bc"'), 169 ("a bc", '"a bc"'), 170 (" bc", '" bc"'), 171 ] 172 else: 173 pairs = [ 174 ("abc", "abc"), 175 (" abc", r"\ abc"), 176 ("a bc", r"a\ bc"), 177 ("a bc", r"a\ \ bc"), 178 (" bc", r"\ \ bc"), 179 # On posix, we also protect parens and other special characters. 180 ("a(bc", r"a\(bc"), 181 ("a)bc", r"a\)bc"), 182 ("a( )bc", r"a\(\ \)bc"), 183 ("a[1]bc", r"a\[1\]bc"), 184 ("a{1}bc", r"a\{1\}bc"), 185 ("a#bc", r"a\#bc"), 186 ("a?bc", r"a\?bc"), 187 ("a=bc", r"a\=bc"), 188 ("a\\bc", r"a\\bc"), 189 ("a|bc", r"a\|bc"), 190 ("a;bc", r"a\;bc"), 191 ("a:bc", r"a\:bc"), 192 ("a'bc", r"a\'bc"), 193 ("a*bc", r"a\*bc"), 194 ('a"bc', r"a\"bc"), 195 ("a^bc", r"a\^bc"), 196 ("a&bc", r"a\&bc"), 197 ] 198 199 200 @pytest.mark.parametrize("s1,expected", pairs) 201 def test_protect_filename(s1, expected): 202 assert completer.protect_filename(s1) == expected 203 204 205 def check_line_split(splitter, test_specs): 206 for part1, part2, split in test_specs: 207 cursor_pos = len(part1) 208 line = part1 + part2 209 out = splitter.split_line(line, cursor_pos) 210 assert out == split 211 212 def test_line_split(): 213 """Basic line splitter test with default specs.""" 214 sp = completer.CompletionSplitter() 215 # The format of the test specs is: part1, part2, expected answer. Parts 1 216 # and 2 are joined into the 'line' sent to the splitter, as if the cursor 217 # was at the end of part1. So an empty part2 represents someone hitting 218 # tab at the end of the line, the most common case. 219 t = [ 220 ("run some/script", "", "some/script"), 221 ("run scripts/er", "ror.py foo", "scripts/er"), 222 ("echo $HOM", "", "HOM"), 223 ("print sys.pa", "", "sys.pa"), 224 ("print(sys.pa", "", "sys.pa"), 225 ("execfile('scripts/er", "", "scripts/er"), 226 ("a[x.", "", "x."), 227 ("a[x.", "y", "x."), 228 ('cd "some_file/', "", "some_file/"), 229 ] 230 check_line_split(sp, t) 231 # Ensure splitting works OK with unicode by re-running the tests with 232 # all inputs turned into unicode 233 check_line_split(sp, [map(str, p) for p in t]) 234 235 236 class NamedInstanceClass: 237 instances = {} 238 239 def __init__(self, name): 240 self.instances[name] = self 241 242 @classmethod 243 def _ipython_key_completions_(cls): 244 return cls.instances.keys() 245 246 247 class KeyCompletable: 248 def __init__(self, things=()): 249 self.things = things 250 251 def _ipython_key_completions_(self): 252 return list(self.things) 253 254 255 class TestCompleter(unittest.TestCase): 256 def setUp(self): 257 """ 258 We want to silence all PendingDeprecationWarning when testing the completer 259 """ 260 self._assertwarns = self.assertWarns(PendingDeprecationWarning) 261 self._assertwarns.__enter__() 262 263 def tearDown(self): 264 try: 265 self._assertwarns.__exit__(None, None, None) 266 except AssertionError: 267 pass 268 269 def test_custom_completion_error(self): 270 """Test that errors from custom attribute completers are silenced.""" 271 ip = get_ipython() 272 273 class A: 274 pass 275 276 ip.user_ns["x"] = A() 277 278 @complete_object.register(A) 279 def complete_A(a, existing_completions): 280 raise TypeError("this should be silenced") 281 282 ip.complete("x.") 283 284 def test_custom_completion_ordering(self): 285 """Test that errors from custom attribute completers are silenced.""" 286 ip = get_ipython() 287 288 _, matches = ip.complete('in') 289 assert matches.index('input') < matches.index('int') 290 291 def complete_example(a): 292 return ['example2', 'example1'] 293 294 ip.Completer.custom_completers.add_re('ex*', complete_example) 295 _, matches = ip.complete('ex') 296 assert matches.index('example2') < matches.index('example1') 297 298 def test_unicode_completions(self): 299 ip = get_ipython() 300 # Some strings that trigger different types of completion. Check them both 301 # in str and unicode forms 302 s = ["ru", "%ru", "cd /", "floa", "float(x)/"] 303 for t in s + list(map(str, s)): 304 # We don't need to check exact completion values (they may change 305 # depending on the state of the namespace, but at least no exceptions 306 # should be thrown and the return value should be a pair of text, list 307 # values. 308 text, matches = ip.complete(t) 309 self.assertIsInstance(text, str) 310 self.assertIsInstance(matches, list) 311 312 def test_latex_completions(self): 313 314 ip = get_ipython() 315 # Test some random unicode symbols 316 keys = random.sample(sorted(latex_symbols), 10) 317 for k in keys: 318 text, matches = ip.complete(k) 319 self.assertEqual(text, k) 320 self.assertEqual(matches, [latex_symbols[k]]) 321 # Test a more complex line 322 text, matches = ip.complete("print(\\alpha") 323 self.assertEqual(text, "\\alpha") 324 self.assertEqual(matches[0], latex_symbols["\\alpha"]) 325 # Test multiple matching latex symbols 326 text, matches = ip.complete("\\al") 327 self.assertIn("\\alpha", matches) 328 self.assertIn("\\aleph", matches) 329 330 def test_latex_no_results(self): 331 """ 332 forward latex should really return nothing in either field if nothing is found. 333 """ 334 ip = get_ipython() 335 text, matches = ip.Completer.latex_matches("\\really_i_should_match_nothing") 336 self.assertEqual(text, "") 337 self.assertEqual(matches, ()) 338 339 def test_back_latex_completion(self): 340 ip = get_ipython() 341 342 # do not return more than 1 matches for \beta, only the latex one. 343 name, matches = ip.complete("\\β") 344 self.assertEqual(matches, ["\\beta"]) 345 346 def test_back_unicode_completion(self): 347 ip = get_ipython() 348 349 name, matches = ip.complete("\\Ⅴ") 350 self.assertEqual(matches, ["\\ROMAN NUMERAL FIVE"]) 351 352 def test_forward_unicode_completion(self): 353 ip = get_ipython() 354 355 name, matches = ip.complete("\\ROMAN NUMERAL FIVE") 356 self.assertEqual(matches, ["Ⅴ"]) # This is not a V 357 self.assertEqual(matches, ["\u2164"]) # same as above but explicit. 358 359 def test_delim_setting(self): 360 sp = completer.CompletionSplitter() 361 sp.delims = " " 362 self.assertEqual(sp.delims, " ") 363 self.assertEqual(sp._delim_expr, r"[\ ]") 364 365 def test_spaces(self): 366 """Test with only spaces as split chars.""" 367 sp = completer.CompletionSplitter() 368 sp.delims = " " 369 t = [("foo", "", "foo"), ("run foo", "", "foo"), ("run foo", "bar", "foo")] 370 check_line_split(sp, t) 371 372 def test_has_open_quotes1(self): 373 for s in ["'", "'''", "'hi' '"]: 374 self.assertEqual(completer.has_open_quotes(s), "'") 375 376 def test_has_open_quotes2(self): 377 for s in ['"', '"""', '"hi" "']: 378 self.assertEqual(completer.has_open_quotes(s), '"') 379 380 def test_has_open_quotes3(self): 381 for s in ["''", "''' '''", "'hi' 'ipython'"]: 382 self.assertFalse(completer.has_open_quotes(s)) 383 384 def test_has_open_quotes4(self): 385 for s in ['""', '""" """', '"hi" "ipython"']: 386 self.assertFalse(completer.has_open_quotes(s)) 387 388 @pytest.mark.xfail( 389 sys.platform == "win32", reason="abspath completions fail on Windows" 390 ) 391 def test_abspath_file_completions(self): 392 ip = get_ipython() 393 with TemporaryDirectory() as tmpdir: 394 prefix = os.path.join(tmpdir, "foo") 395 suffixes = ["1", "2"] 396 names = [prefix + s for s in suffixes] 397 for n in names: 398 open(n, "w", encoding="utf-8").close() 399 400 # Check simple completion 401 c = ip.complete(prefix)[1] 402 self.assertEqual(c, names) 403 404 # Now check with a function call 405 cmd = 'a = f("%s' % prefix 406 c = ip.complete(prefix, cmd)[1] 407 comp = [prefix + s for s in suffixes] 408 self.assertEqual(c, comp) 409 410 def test_local_file_completions(self): 411 ip = get_ipython() 412 with TemporaryWorkingDirectory(): 413 prefix = "./foo" 414 suffixes = ["1", "2"] 415 names = [prefix + s for s in suffixes] 416 for n in names: 417 open(n, "w", encoding="utf-8").close() 418 419 # Check simple completion 420 c = ip.complete(prefix)[1] 421 self.assertEqual(c, names) 422 423 # Now check with a function call 424 cmd = 'a = f("%s' % prefix 425 c = ip.complete(prefix, cmd)[1] 426 comp = {prefix + s for s in suffixes} 427 self.assertTrue(comp.issubset(set(c))) 428 429 def test_quoted_file_completions(self): 430 ip = get_ipython() 431 432 def _(text): 433 return ip.Completer._complete( 434 cursor_line=0, cursor_pos=len(text), full_text=text 435 )["IPCompleter.file_matcher"]["completions"] 436 437 with TemporaryWorkingDirectory(): 438 name = "foo'bar" 439 open(name, "w", encoding="utf-8").close() 440 441 # Don't escape Windows 442 escaped = name if sys.platform == "win32" else "foo\\'bar" 443 444 # Single quote matches embedded single quote 445 c = _("open('foo")[0] 446 self.assertEqual(c.text, escaped) 447 448 # Double quote requires no escape 449 c = _('open("foo')[0] 450 self.assertEqual(c.text, name) 451 452 # No quote requires an escape 453 c = _("%ls foo")[0] 454 self.assertEqual(c.text, escaped) 455 456 @pytest.mark.xfail( 457 sys.version_info.releaselevel in ("alpha",), 458 reason="Parso does not yet parse 3.13", 459 ) 460 def test_all_completions_dups(self): 461 """ 462 Make sure the output of `IPCompleter.all_completions` does not have 463 duplicated prefixes. 464 """ 465 ip = get_ipython() 466 c = ip.Completer 467 ip.ex("class TestClass():\n\ta=1\n\ta1=2") 468 for jedi_status in [True, False]: 469 with provisionalcompleter(): 470 ip.Completer.use_jedi = jedi_status 471 matches = c.all_completions("TestCl") 472 assert matches == ["TestClass"], (jedi_status, matches) 473 matches = c.all_completions("TestClass.") 474 assert len(matches) > 2, (jedi_status, matches) 475 matches = c.all_completions("TestClass.a") 476 if jedi_status: 477 assert matches == ["TestClass.a", "TestClass.a1"], jedi_status 478 else: 479 assert matches == [".a", ".a1"], jedi_status 480 481 @pytest.mark.xfail( 482 sys.version_info.releaselevel in ("alpha",), 483 reason="Parso does not yet parse 3.13", 484 ) 485 def test_jedi(self): 486 """ 487 A couple of issue we had with Jedi 488 """ 489 ip = get_ipython() 490 491 def _test_complete(reason, s, comp, start=None, end=None): 492 l = len(s) 493 start = start if start is not None else l 494 end = end if end is not None else l 495 with provisionalcompleter(): 496 ip.Completer.use_jedi = True 497 completions = set(ip.Completer.completions(s, l)) 498 ip.Completer.use_jedi = False 499 assert Completion(start, end, comp) in completions, reason 500 501 def _test_not_complete(reason, s, comp): 502 l = len(s) 503 with provisionalcompleter(): 504 ip.Completer.use_jedi = True 505 completions = set(ip.Completer.completions(s, l)) 506 ip.Completer.use_jedi = False 507 assert Completion(l, l, comp) not in completions, reason 508 509 import jedi 510 511 jedi_version = tuple(int(i) for i in jedi.__version__.split(".")[:3]) 512 if jedi_version > (0, 10): 513 _test_complete("jedi >0.9 should complete and not crash", "a=1;a.", "real") 514 _test_complete("can infer first argument", 'a=(1,"foo");a[0].', "real") 515 _test_complete("can infer second argument", 'a=(1,"foo");a[1].', "capitalize") 516 _test_complete("cover duplicate completions", "im", "import", 0, 2) 517 518 _test_not_complete("does not mix types", 'a=(1,"foo");a[0].', "capitalize") 519 520 @pytest.mark.xfail( 521 sys.version_info.releaselevel in ("alpha",), 522 reason="Parso does not yet parse 3.13", 523 ) 524 def test_completion_have_signature(self): 525 """ 526 Lets make sure jedi is capable of pulling out the signature of the function we are completing. 527 """ 528 ip = get_ipython() 529 with provisionalcompleter(): 530 ip.Completer.use_jedi = True 531 completions = ip.Completer.completions("ope", 3) 532 c = next(completions) # should be `open` 533 ip.Completer.use_jedi = False 534 assert "file" in c.signature, "Signature of function was not found by completer" 535 assert ( 536 "encoding" in c.signature 537 ), "Signature of function was not found by completer" 538 539 @pytest.mark.xfail( 540 sys.version_info.releaselevel in ("alpha",), 541 reason="Parso does not yet parse 3.13", 542 ) 543 def test_completions_have_type(self): 544 """ 545 Lets make sure matchers provide completion type. 546 """ 547 ip = get_ipython() 548 with provisionalcompleter(): 549 ip.Completer.use_jedi = False 550 completions = ip.Completer.completions("%tim", 3) 551 c = next(completions) # should be `%time` or similar 552 assert c.type == "magic", "Type of magic was not assigned by completer" 553 554 @pytest.mark.xfail( 555 parse(version("jedi")) <= parse("0.18.0"), 556 reason="Known failure on jedi<=0.18.0", 557 strict=True, 558 ) 559 def test_deduplicate_completions(self): 560 """ 561 Test that completions are correctly deduplicated (even if ranges are not the same) 562 """ 563 ip = get_ipython() 564 ip.ex( 565 textwrap.dedent( 566 """ 567 class Z: 568 zoo = 1 569 """ 570 ) 571 ) 572 with provisionalcompleter(): 573 ip.Completer.use_jedi = True 574 l = list( 575 _deduplicate_completions("Z.z", ip.Completer.completions("Z.z", 3)) 576 ) 577 ip.Completer.use_jedi = False 578 579 assert len(l) == 1, "Completions (Z.z<tab>) correctly deduplicate: %s " % l 580 assert l[0].text == "zoo" # and not `it.accumulate` 581 582 @pytest.mark.xfail( 583 sys.version_info.releaselevel in ("alpha",), 584 reason="Parso does not yet parse 3.13", 585 ) 586 def test_greedy_completions(self): 587 """ 588 Test the capability of the Greedy completer. 589 590 Most of the test here does not really show off the greedy completer, for proof 591 each of the text below now pass with Jedi. The greedy completer is capable of more. 592 593 See the :any:`test_dict_key_completion_contexts` 594 595 """ 596 ip = get_ipython() 597 ip.ex("a=list(range(5))") 598 ip.ex("d = {'a b': str}") 599 _, c = ip.complete(".", line="a[0].") 600 self.assertFalse(".real" in c, "Shouldn't have completed on a[0]: %s" % c) 601 602 def _(line, cursor_pos, expect, message, completion): 603 with greedy_completion(), provisionalcompleter(): 604 ip.Completer.use_jedi = False 605 _, c = ip.complete(".", line=line, cursor_pos=cursor_pos) 606 self.assertIn(expect, c, message % c) 607 608 ip.Completer.use_jedi = True 609 with provisionalcompleter(): 610 completions = ip.Completer.completions(line, cursor_pos) 611 self.assertIn(completion, list(completions)) 612 613 with provisionalcompleter(): 614 _( 615 "a[0].", 616 5, 617 ".real", 618 "Should have completed on a[0].: %s", 619 Completion(5, 5, "real"), 620 ) 621 _( 622 "a[0].r", 623 6, 624 ".real", 625 "Should have completed on a[0].r: %s", 626 Completion(5, 6, "real"), 627 ) 628 _( 629 "a[0].from_", 630 10, 631 ".from_bytes", 632 "Should have completed on a[0].from_: %s", 633 Completion(5, 10, "from_bytes"), 634 ) 635 _( 636 "assert str.star", 637 14, 638 ".startswith", 639 "Should have completed on `assert str.star`: %s", 640 Completion(11, 14, "startswith"), 641 ) 642 _( 643 "d['a b'].str", 644 12, 645 ".strip", 646 "Should have completed on `d['a b'].str`: %s", 647 Completion(9, 12, "strip"), 648 ) 649 _( 650 "a.app", 651 4, 652 ".append", 653 "Should have completed on `a.app`: %s", 654 Completion(2, 4, "append"), 655 ) 656 657 def test_omit__names(self): 658 # also happens to test IPCompleter as a configurable 659 ip = get_ipython() 660 ip._hidden_attr = 1 661 ip._x = {} 662 c = ip.Completer 663 ip.ex("ip=get_ipython()") 664 cfg = Config() 665 cfg.IPCompleter.omit__names = 0 666 c.update_config(cfg) 667 with provisionalcompleter(): 668 c.use_jedi = False 669 s, matches = c.complete("ip.") 670 self.assertIn(".__str__", matches) 671 self.assertIn("._hidden_attr", matches) 672 673 # c.use_jedi = True 674 # completions = set(c.completions('ip.', 3)) 675 # self.assertIn(Completion(3, 3, '__str__'), completions) 676 # self.assertIn(Completion(3,3, "_hidden_attr"), completions) 677 678 cfg = Config() 679 cfg.IPCompleter.omit__names = 1 680 c.update_config(cfg) 681 with provisionalcompleter(): 682 c.use_jedi = False 683 s, matches = c.complete("ip.") 684 self.assertNotIn(".__str__", matches) 685 # self.assertIn('ip._hidden_attr', matches) 686 687 # c.use_jedi = True 688 # completions = set(c.completions('ip.', 3)) 689 # self.assertNotIn(Completion(3,3,'__str__'), completions) 690 # self.assertIn(Completion(3,3, "_hidden_attr"), completions) 691 692 cfg = Config() 693 cfg.IPCompleter.omit__names = 2 694 c.update_config(cfg) 695 with provisionalcompleter(): 696 c.use_jedi = False 697 s, matches = c.complete("ip.") 698 self.assertNotIn(".__str__", matches) 699 self.assertNotIn("._hidden_attr", matches) 700 701 # c.use_jedi = True 702 # completions = set(c.completions('ip.', 3)) 703 # self.assertNotIn(Completion(3,3,'__str__'), completions) 704 # self.assertNotIn(Completion(3,3, "_hidden_attr"), completions) 705 706 with provisionalcompleter(): 707 c.use_jedi = False 708 s, matches = c.complete("ip._x.") 709 self.assertIn(".keys", matches) 710 711 # c.use_jedi = True 712 # completions = set(c.completions('ip._x.', 6)) 713 # self.assertIn(Completion(6,6, "keys"), completions) 714 715 del ip._hidden_attr 716 del ip._x 717 718 def test_limit_to__all__False_ok(self): 719 """ 720 Limit to all is deprecated, once we remove it this test can go away. 721 """ 722 ip = get_ipython() 723 c = ip.Completer 724 c.use_jedi = False 725 ip.ex("class D: x=24") 726 ip.ex("d=D()") 727 cfg = Config() 728 cfg.IPCompleter.limit_to__all__ = False 729 c.update_config(cfg) 730 s, matches = c.complete("d.") 731 self.assertIn(".x", matches) 732 733 def test_get__all__entries_ok(self): 734 class A: 735 __all__ = ["x", 1] 736 737 words = completer.get__all__entries(A()) 738 self.assertEqual(words, ["x"]) 739 740 def test_get__all__entries_no__all__ok(self): 741 class A: 742 pass 743 744 words = completer.get__all__entries(A()) 745 self.assertEqual(words, []) 746 747 def test_completes_globals_as_args_of_methods(self): 748 ip = get_ipython() 749 c = ip.Completer 750 c.use_jedi = False 751 ip.ex("long_variable_name = 1") 752 ip.ex("a = []") 753 s, matches = c.complete(None, "a.sort(lo") 754 self.assertIn("long_variable_name", matches) 755 756 def test_completes_attributes_in_fstring_expressions(self): 757 ip = get_ipython() 758 c = ip.Completer 759 c.use_jedi = False 760 761 class CustomClass: 762 def method_one(self): 763 pass 764 765 ip.user_ns["custom_obj"] = CustomClass() 766 767 # Test completion inside f-string expressions 768 s, matches = c.complete(None, "f'{custom_obj.meth") 769 self.assertIn(".method_one", matches) 770 771 def test_completes_in_dict_expressions(self): 772 ip = get_ipython() 773 c = ip.Completer 774 c.use_jedi = False 775 ip.ex("class Test: pass") 776 ip.ex("test_obj = Test()") 777 ip.ex("test_obj.attribute = 'value'") 778 779 # Test completion in dictionary expressions 780 s, matches = c.complete(None, "d = {'key': test_obj.attr") 781 self.assertIn(".attribute", matches) 782 783 # Test global completion in dictionary expressions with dots 784 s, matches = c.complete(None, "d = {'k.e.y': Te") 785 self.assertIn("Test", matches) 786 787 def test_func_kw_completions(self): 788 ip = get_ipython() 789 c = ip.Completer 790 c.use_jedi = False 791 ip.ex("def myfunc(a=1,b=2): return a+b") 792 s, matches = c.complete(None, "myfunc(1,b") 793 self.assertIn("b=", matches) 794 # Simulate completing with cursor right after b (pos==10): 795 s, matches = c.complete(None, "myfunc(1,b)", 10) 796 self.assertIn("b=", matches) 797 s, matches = c.complete(None, 'myfunc(a="escaped\\")string",b') 798 self.assertIn("b=", matches) 799 # builtin function 800 s, matches = c.complete(None, "min(k, k") 801 self.assertIn("key=", matches) 802 803 def test_default_arguments_from_docstring(self): 804 ip = get_ipython() 805 c = ip.Completer 806 kwd = c._default_arguments_from_docstring("min(iterable[, key=func]) -> value") 807 self.assertEqual(kwd, ["key"]) 808 # with cython type etc 809 kwd = c._default_arguments_from_docstring( 810 "Minuit.migrad(self, int ncall=10000, resume=True, int nsplit=1)\n" 811 ) 812 self.assertEqual(kwd, ["ncall", "resume", "nsplit"]) 813 # white spaces 814 kwd = c._default_arguments_from_docstring( 815 "\n Minuit.migrad(self, int ncall=10000, resume=True, int nsplit=1)\n" 816 ) 817 self.assertEqual(kwd, ["ncall", "resume", "nsplit"]) 818 819 def test_line_magics(self): 820 ip = get_ipython() 821 c = ip.Completer 822 s, matches = c.complete(None, "lsmag") 823 self.assertIn("%lsmagic", matches) 824 s, matches = c.complete(None, "%lsmag") 825 self.assertIn("%lsmagic", matches) 826 827 def test_cell_magics(self): 828 from IPython.core.magic import register_cell_magic 829 830 @register_cell_magic 831 def _foo_cellm(line, cell): 832 pass 833 834 ip = get_ipython() 835 c = ip.Completer 836 837 s, matches = c.complete(None, "_foo_ce") 838 self.assertIn("%%_foo_cellm", matches) 839 s, matches = c.complete(None, "%%_foo_ce") 840 self.assertIn("%%_foo_cellm", matches) 841 842 def test_line_cell_magics(self): 843 from IPython.core.magic import register_line_cell_magic 844 845 @register_line_cell_magic 846 def _bar_cellm(line, cell): 847 pass 848 849 ip = get_ipython() 850 c = ip.Completer 851 852 # The policy here is trickier, see comments in completion code. The 853 # returned values depend on whether the user passes %% or not explicitly, 854 # and this will show a difference if the same name is both a line and cell 855 # magic. 856 s, matches = c.complete(None, "_bar_ce") 857 self.assertIn("%_bar_cellm", matches) 858 self.assertIn("%%_bar_cellm", matches) 859 s, matches = c.complete(None, "%_bar_ce") 860 self.assertIn("%_bar_cellm", matches) 861 self.assertIn("%%_bar_cellm", matches) 862 s, matches = c.complete(None, "%%_bar_ce") 863 self.assertNotIn("%_bar_cellm", matches) 864 self.assertIn("%%_bar_cellm", matches) 865 866 def test_magic_completion_order(self): 867 ip = get_ipython() 868 c = ip.Completer 869 870 # Test ordering of line and cell magics. 871 text, matches = c.complete("timeit") 872 self.assertEqual(matches, ["%timeit", "%%timeit"]) 873 874 def test_magic_completion_shadowing(self): 875 ip = get_ipython() 876 c = ip.Completer 877 c.use_jedi = False 878 879 # Before importing matplotlib, %matplotlib magic should be the only option. 880 text, matches = c.complete("mat") 881 self.assertEqual(matches, ["%matplotlib"]) 882 883 # The newly introduced name should shadow the magic. 884 ip.run_cell("matplotlib = 1") 885 text, matches = c.complete("mat") 886 self.assertEqual(matches, ["matplotlib"]) 887 888 # After removing matplotlib from namespace, the magic should again be 889 # the only option. 890 del ip.user_ns["matplotlib"] 891 text, matches = c.complete("mat") 892 self.assertEqual(matches, ["%matplotlib"]) 893 894 def test_magic_completion_shadowing_explicit(self): 895 """ 896 If the user try to complete a shadowed magic, and explicit % start should 897 still return the completions. 898 """ 899 ip = get_ipython() 900 c = ip.Completer 901 902 # Before importing matplotlib, %matplotlib magic should be the only option. 903 text, matches = c.complete("%mat") 904 self.assertEqual(matches, ["%matplotlib"]) 905 906 ip.run_cell("matplotlib = 1") 907 908 # After removing matplotlib from namespace, the magic should still be 909 # the only option. 910 text, matches = c.complete("%mat") 911 self.assertEqual(matches, ["%matplotlib"]) 912 913 def test_magic_config(self): 914 ip = get_ipython() 915 c = ip.Completer 916 917 s, matches = c.complete(None, "conf") 918 self.assertIn("%config", matches) 919 s, matches = c.complete(None, "conf") 920 self.assertNotIn("AliasManager", matches) 921 s, matches = c.complete(None, "config ") 922 self.assertIn("AliasManager", matches) 923 s, matches = c.complete(None, "%config ") 924 self.assertIn("AliasManager", matches) 925 s, matches = c.complete(None, "config Ali") 926 self.assertListEqual(["AliasManager"], matches) 927 s, matches = c.complete(None, "%config Ali") 928 self.assertListEqual(["AliasManager"], matches) 929 s, matches = c.complete(None, "config AliasManager") 930 self.assertListEqual(["AliasManager"], matches) 931 s, matches = c.complete(None, "%config AliasManager") 932 self.assertListEqual(["AliasManager"], matches) 933 s, matches = c.complete(None, "config AliasManager.") 934 self.assertIn("AliasManager.default_aliases", matches) 935 s, matches = c.complete(None, "%config AliasManager.") 936 self.assertIn("AliasManager.default_aliases", matches) 937 s, matches = c.complete(None, "config AliasManager.de") 938 self.assertListEqual(["AliasManager.default_aliases"], matches) 939 s, matches = c.complete(None, "config AliasManager.de") 940 self.assertListEqual(["AliasManager.default_aliases"], matches) 941 942 def test_magic_color(self): 943 ip = get_ipython() 944 c = ip.Completer 945 946 s, matches = c.complete(None, "colo") 947 self.assertIn("%colors", matches) 948 s, matches = c.complete(None, "colo") 949 self.assertNotIn("NoColor", matches) 950 s, matches = c.complete(None, "%colors") # No trailing space 951 self.assertNotIn("NoColor", matches) 952 s, matches = c.complete(None, "colors ") 953 self.assertIn("NoColor", matches) 954 s, matches = c.complete(None, "%colors ") 955 self.assertIn("NoColor", matches) 956 s, matches = c.complete(None, "colors NoCo") 957 self.assertListEqual(["NoColor"], matches) 958 s, matches = c.complete(None, "%colors NoCo") 959 self.assertListEqual(["NoColor"], matches) 960 961 def test_match_dict_keys(self): 962 """ 963 Test that match_dict_keys works on a couple of use case does return what 964 expected, and does not crash 965 """ 966 delims = " \t\n`!@#$^&*()=+[{]}\\|;:'\",<>?" 967 968 def match(*args, **kwargs): 969 quote, offset, matches = match_dict_keys(*args, delims=delims, **kwargs) 970 return quote, offset, list(matches) 971 972 keys = ["foo", b"far"] 973 assert match(keys, "b'") == ("'", 2, ["far"]) 974 assert match(keys, "b'f") == ("'", 2, ["far"]) 975 assert match(keys, 'b"') == ('"', 2, ["far"]) 976 assert match(keys, 'b"f') == ('"', 2, ["far"]) 977 978 assert match(keys, "'") == ("'", 1, ["foo"]) 979 assert match(keys, "'f") == ("'", 1, ["foo"]) 980 assert match(keys, '"') == ('"', 1, ["foo"]) 981 assert match(keys, '"f') == ('"', 1, ["foo"]) 982 983 # Completion on first item of tuple 984 keys = [("foo", 1111), ("foo", 2222), (3333, "bar"), (3333, "test")] 985 assert match(keys, "'f") == ("'", 1, ["foo"]) 986 assert match(keys, "33") == ("", 0, ["3333"]) 987 988 # Completion on numbers 989 keys = [ 990 0xDEADBEEF, 991 1111, 992 1234, 993 "1999", 994 0b10101, 995 22, 996 ] # 0xDEADBEEF = 3735928559; 0b10101 = 21 997 assert match(keys, "0xdead") == ("", 0, ["0xdeadbeef"]) 998 assert match(keys, "1") == ("", 0, ["1111", "1234"]) 999 assert match(keys, "2") == ("", 0, ["21", "22"]) 1000 assert match(keys, "0b101") == ("", 0, ["0b10101", "0b10110"]) 1001 1002 # Should yield on variables 1003 assert match(keys, "a_variable") == ("", 0, []) 1004 1005 # Should pass over invalid literals 1006 assert match(keys, "'' ''") == ("", 0, []) 1007 1008 def test_match_dict_keys_tuple(self): 1009 """ 1010 Test that match_dict_keys called with extra prefix works on a couple of use case, 1011 does return what expected, and does not crash. 1012 """ 1013 delims = " \t\n`!@#$^&*()=+[{]}\\|;:'\",<>?" 1014 1015 keys = [("foo", "bar"), ("foo", "oof"), ("foo", b"bar"), ('other', 'test')] 1016 1017 def match(*args, extra=None, **kwargs): 1018 quote, offset, matches = match_dict_keys( 1019 *args, delims=delims, extra_prefix=extra, **kwargs 1020 ) 1021 return quote, offset, list(matches) 1022 1023 # Completion on first key == "foo" 1024 assert match(keys, "'", extra=("foo",)) == ("'", 1, ["bar", "oof"]) 1025 assert match(keys, '"', extra=("foo",)) == ('"', 1, ["bar", "oof"]) 1026 assert match(keys, "'o", extra=("foo",)) == ("'", 1, ["oof"]) 1027 assert match(keys, '"o', extra=("foo",)) == ('"', 1, ["oof"]) 1028 assert match(keys, "b'", extra=("foo",)) == ("'", 2, ["bar"]) 1029 assert match(keys, 'b"', extra=("foo",)) == ('"', 2, ["bar"]) 1030 assert match(keys, "b'b", extra=("foo",)) == ("'", 2, ["bar"]) 1031 assert match(keys, 'b"b', extra=("foo",)) == ('"', 2, ["bar"]) 1032 1033 # No Completion 1034 assert match(keys, "'", extra=("no_foo",)) == ("'", 1, []) 1035 assert match(keys, "'", extra=("fo",)) == ("'", 1, []) 1036 1037 keys = [("foo1", "foo2", "foo3", "foo4"), ("foo1", "foo2", "bar", "foo4")] 1038 assert match(keys, "'foo", extra=("foo1",)) == ("'", 1, ["foo2"]) 1039 assert match(keys, "'foo", extra=("foo1", "foo2")) == ("'", 1, ["foo3"]) 1040 assert match(keys, "'foo", extra=("foo1", "foo2", "foo3")) == ("'", 1, ["foo4"]) 1041 assert match(keys, "'foo", extra=("foo1", "foo2", "foo3", "foo4")) == ( 1042 "'", 1043 1, 1044 [], 1045 ) 1046 1047 keys = [("foo", 1111), ("foo", "2222"), (3333, "bar"), (3333, 4444)] 1048 assert match(keys, "'", extra=("foo",)) == ("'", 1, ["2222"]) 1049 assert match(keys, "", extra=("foo",)) == ("", 0, ["1111", "'2222'"]) 1050 assert match(keys, "'", extra=(3333,)) == ("'", 1, ["bar"]) 1051 assert match(keys, "", extra=(3333,)) == ("", 0, ["'bar'", "4444"]) 1052 assert match(keys, "'", extra=("3333",)) == ("'", 1, []) 1053 assert match(keys, "33") == ("", 0, ["3333"]) 1054 1055 def test_dict_key_completion_closures(self): 1056 ip = get_ipython() 1057 complete = ip.Completer.complete 1058 ip.Completer.auto_close_dict_keys = True 1059 1060 ip.user_ns["d"] = { 1061 # tuple only 1062 ("aa", 11): None, 1063 # tuple and non-tuple 1064 ("bb", 22): None, 1065 "bb": None, 1066 # non-tuple only 1067 "cc": None, 1068 # numeric tuple only 1069 (77, "x"): None, 1070 # numeric tuple and non-tuple 1071 (88, "y"): None, 1072 88: None, 1073 # numeric non-tuple only 1074 99: None, 1075 } 1076 1077 _, matches = complete(line_buffer="d[") 1078 # should append `, ` if matches a tuple only 1079 self.assertIn("'aa', ", matches) 1080 # should not append anything if matches a tuple and an item 1081 self.assertIn("'bb'", matches) 1082 # should append `]` if matches and item only 1083 self.assertIn("'cc']", matches) 1084 1085 # should append `, ` if matches a tuple only 1086 self.assertIn("77, ", matches) 1087 # should not append anything if matches a tuple and an item 1088 self.assertIn("88", matches) 1089 # should append `]` if matches and item only 1090 self.assertIn("99]", matches) 1091 1092 _, matches = complete(line_buffer="d['aa', ") 1093 # should restrict matches to those matching tuple prefix 1094 self.assertIn("11]", matches) 1095 self.assertNotIn("'bb'", matches) 1096 self.assertNotIn("'bb', ", matches) 1097 self.assertNotIn("'bb']", matches) 1098 self.assertNotIn("'cc'", matches) 1099 self.assertNotIn("'cc', ", matches) 1100 self.assertNotIn("'cc']", matches) 1101 ip.Completer.auto_close_dict_keys = False 1102 1103 def test_dict_key_completion_string(self): 1104 """Test dictionary key completion for string keys""" 1105 ip = get_ipython() 1106 complete = ip.Completer.complete 1107 1108 ip.user_ns["d"] = {"abc": None} 1109 1110 # check completion at different stages 1111 _, matches = complete(line_buffer="d[") 1112 self.assertIn("'abc'", matches) 1113 self.assertNotIn("'abc']", matches) 1114 1115 _, matches = complete(line_buffer="d['") 1116 self.assertIn("abc", matches) 1117 self.assertNotIn("abc']", matches) 1118 1119 _, matches = complete(line_buffer="d['a") 1120 self.assertIn("abc", matches) 1121 self.assertNotIn("abc']", matches) 1122 1123 # check use of different quoting 1124 _, matches = complete(line_buffer='d["') 1125 self.assertIn("abc", matches) 1126 self.assertNotIn('abc"]', matches) 1127 1128 _, matches = complete(line_buffer='d["a') 1129 self.assertIn("abc", matches) 1130 self.assertNotIn('abc"]', matches) 1131 1132 # check sensitivity to following context 1133 _, matches = complete(line_buffer="d[]", cursor_pos=2) 1134 self.assertIn("'abc'", matches) 1135 1136 _, matches = complete(line_buffer="d['']", cursor_pos=3) 1137 self.assertIn("abc", matches) 1138 self.assertNotIn("abc'", matches) 1139 self.assertNotIn("abc']", matches) 1140 1141 # check multiple solutions are correctly returned and that noise is not 1142 ip.user_ns["d"] = { 1143 "abc": None, 1144 "abd": None, 1145 "bad": None, 1146 object(): None, 1147 5: None, 1148 ("abe", None): None, 1149 (None, "abf"): None 1150 } 1151 1152 _, matches = complete(line_buffer="d['a") 1153 self.assertIn("abc", matches) 1154 self.assertIn("abd", matches) 1155 self.assertNotIn("bad", matches) 1156 self.assertNotIn("abe", matches) 1157 self.assertNotIn("abf", matches) 1158 assert not any(m.endswith(("]", '"', "'")) for m in matches), matches 1159 1160 # check escaping and whitespace 1161 ip.user_ns["d"] = {"a\nb": None, "a'b": None, 'a"b': None, "a word": None} 1162 _, matches = complete(line_buffer="d['a") 1163 self.assertIn("a\\nb", matches) 1164 self.assertIn("a\\'b", matches) 1165 self.assertIn('a"b', matches) 1166 self.assertIn("a word", matches) 1167 assert not any(m.endswith(("]", '"', "'")) for m in matches), matches 1168 1169 # - can complete on non-initial word of the string 1170 _, matches = complete(line_buffer="d['a w") 1171 self.assertIn("word", matches) 1172 1173 # - understands quote escaping 1174 _, matches = complete(line_buffer="d['a\\'") 1175 self.assertIn("b", matches) 1176 1177 # - default quoting should work like repr 1178 _, matches = complete(line_buffer="d[") 1179 self.assertIn('"a\'b"', matches) 1180 1181 # - when opening quote with ", possible to match with unescaped apostrophe 1182 _, matches = complete(line_buffer="d[\"a'") 1183 self.assertIn("b", matches) 1184 1185 # need to not split at delims that readline won't split at 1186 if "-" not in ip.Completer.splitter.delims: 1187 ip.user_ns["d"] = {"before-after": None} 1188 _, matches = complete(line_buffer="d['before-af") 1189 self.assertIn("before-after", matches) 1190 1191 # check completion on tuple-of-string keys at different stage - on first key 1192 ip.user_ns["d"] = {('foo', 'bar'): None} 1193 _, matches = complete(line_buffer="d[") 1194 self.assertIn("'foo'", matches) 1195 self.assertNotIn("'foo']", matches) 1196 self.assertNotIn("'bar'", matches) 1197 self.assertNotIn("foo", matches) 1198 self.assertNotIn("bar", matches) 1199 1200 # - match the prefix 1201 _, matches = complete(line_buffer="d['f") 1202 self.assertIn("foo", matches) 1203 self.assertNotIn("foo']", matches) 1204 self.assertNotIn('foo"]', matches) 1205 _, matches = complete(line_buffer="d['foo") 1206 self.assertIn("foo", matches) 1207 1208 # - can complete on second key 1209 _, matches = complete(line_buffer="d['foo', ") 1210 self.assertIn("'bar'", matches) 1211 _, matches = complete(line_buffer="d['foo', 'b") 1212 self.assertIn("bar", matches) 1213 self.assertNotIn("foo", matches) 1214 1215 # - does not propose missing keys 1216 _, matches = complete(line_buffer="d['foo', 'f") 1217 self.assertNotIn("bar", matches) 1218 self.assertNotIn("foo", matches) 1219 1220 # check sensitivity to following context 1221 _, matches = complete(line_buffer="d['foo',]", cursor_pos=8) 1222 self.assertIn("'bar'", matches) 1223 self.assertNotIn("bar", matches) 1224 self.assertNotIn("'foo'", matches) 1225 self.assertNotIn("foo", matches) 1226 1227 _, matches = complete(line_buffer="d['']", cursor_pos=3) 1228 self.assertIn("foo", matches) 1229 assert not any(m.endswith(("]", '"', "'")) for m in matches), matches 1230 1231 _, matches = complete(line_buffer='d[""]', cursor_pos=3) 1232 self.assertIn("foo", matches) 1233 assert not any(m.endswith(("]", '"', "'")) for m in matches), matches 1234 1235 _, matches = complete(line_buffer='d["foo","]', cursor_pos=9) 1236 self.assertIn("bar", matches) 1237 assert not any(m.endswith(("]", '"', "'")) for m in matches), matches 1238 1239 _, matches = complete(line_buffer='d["foo",]', cursor_pos=8) 1240 self.assertIn("'bar'", matches) 1241 self.assertNotIn("bar", matches) 1242 1243 # Can complete with longer tuple keys 1244 ip.user_ns["d"] = {('foo', 'bar', 'foobar'): None} 1245 1246 # - can complete second key 1247 _, matches = complete(line_buffer="d['foo', 'b") 1248 self.assertIn("bar", matches) 1249 self.assertNotIn("foo", matches) 1250 self.assertNotIn("foobar", matches) 1251 1252 # - can complete third key 1253 _, matches = complete(line_buffer="d['foo', 'bar', 'fo") 1254 self.assertIn("foobar", matches) 1255 self.assertNotIn("foo", matches) 1256 self.assertNotIn("bar", matches) 1257 1258 def test_dict_key_completion_numbers(self): 1259 ip = get_ipython() 1260 complete = ip.Completer.complete 1261 1262 ip.user_ns["d"] = { 1263 0xDEADBEEF: None, # 3735928559 1264 1111: None, 1265 1234: None, 1266 "1999": None, 1267 0b10101: None, # 21 1268 22: None, 1269 } 1270 _, matches = complete(line_buffer="d[1") 1271 self.assertIn("1111", matches) 1272 self.assertIn("1234", matches) 1273 self.assertNotIn("1999", matches) 1274 self.assertNotIn("'1999'", matches) 1275 1276 _, matches = complete(line_buffer="d[0xdead") 1277 self.assertIn("0xdeadbeef", matches) 1278 1279 _, matches = complete(line_buffer="d[2") 1280 self.assertIn("21", matches) 1281 self.assertIn("22", matches) 1282 1283 _, matches = complete(line_buffer="d[0b101") 1284 self.assertIn("0b10101", matches) 1285 self.assertIn("0b10110", matches) 1286 1287 def test_dict_key_completion_contexts(self): 1288 """Test expression contexts in which dict key completion occurs""" 1289 ip = get_ipython() 1290 complete = ip.Completer.complete 1291 d = {"abc": None} 1292 ip.user_ns["d"] = d 1293 1294 class C: 1295 data = d 1296 1297 ip.user_ns["C"] = C 1298 ip.user_ns["get"] = lambda: d 1299 ip.user_ns["nested"] = {"x": d} 1300 1301 def assert_no_completion(**kwargs): 1302 _, matches = complete(**kwargs) 1303 self.assertNotIn("abc", matches) 1304 self.assertNotIn("abc'", matches) 1305 self.assertNotIn("abc']", matches) 1306 self.assertNotIn("'abc'", matches) 1307 self.assertNotIn("'abc']", matches) 1308 1309 def assert_completion(**kwargs): 1310 _, matches = complete(**kwargs) 1311 self.assertIn("'abc'", matches) 1312 self.assertNotIn("'abc']", matches) 1313 1314 # no completion after string closed, even if reopened 1315 assert_no_completion(line_buffer="d['a'") 1316 assert_no_completion(line_buffer='d["a"') 1317 assert_no_completion(line_buffer="d['a' + ") 1318 assert_no_completion(line_buffer="d['a' + '") 1319 1320 # completion in non-trivial expressions 1321 assert_completion(line_buffer="+ d[") 1322 assert_completion(line_buffer="(d[") 1323 assert_completion(line_buffer="C.data[") 1324 1325 # nested dict completion 1326 assert_completion(line_buffer="nested['x'][") 1327 1328 with evaluation_policy("minimal"): 1329 with pytest.raises(AssertionError): 1330 assert_completion(line_buffer="nested['x'][") 1331 1332 # greedy flag 1333 def assert_completion(**kwargs): 1334 _, matches = complete(**kwargs) 1335 self.assertIn("get()['abc']", matches) 1336 1337 assert_no_completion(line_buffer="get()[") 1338 with greedy_completion(): 1339 assert_completion(line_buffer="get()[") 1340 assert_completion(line_buffer="get()['") 1341 assert_completion(line_buffer="get()['a") 1342 assert_completion(line_buffer="get()['ab") 1343 assert_completion(line_buffer="get()['abc") 1344 1345 def test_dict_key_completion_bytes(self): 1346 """Test handling of bytes in dict key completion""" 1347 ip = get_ipython() 1348 complete = ip.Completer.complete 1349 1350 ip.user_ns["d"] = {"abc": None, b"abd": None} 1351 1352 _, matches = complete(line_buffer="d[") 1353 self.assertIn("'abc'", matches) 1354 self.assertIn("b'abd'", matches) 1355 1356 if False: # not currently implemented 1357 _, matches = complete(line_buffer="d[b") 1358 self.assertIn("b'abd'", matches) 1359 self.assertNotIn("b'abc'", matches) 1360 1361 _, matches = complete(line_buffer="d[b'") 1362 self.assertIn("abd", matches) 1363 self.assertNotIn("abc", matches) 1364 1365 _, matches = complete(line_buffer="d[B'") 1366 self.assertIn("abd", matches) 1367 self.assertNotIn("abc", matches) 1368 1369 _, matches = complete(line_buffer="d['") 1370 self.assertIn("abc", matches) 1371 self.assertNotIn("abd", matches) 1372 1373 def test_dict_key_completion_unicode_py3(self): 1374 """Test handling of unicode in dict key completion""" 1375 ip = get_ipython() 1376 complete = ip.Completer.complete 1377 1378 ip.user_ns["d"] = {"a\u05d0": None} 1379 1380 # query using escape 1381 if sys.platform != "win32": 1382 # Known failure on Windows 1383 _, matches = complete(line_buffer="d['a\\u05d0") 1384 self.assertIn("u05d0", matches) # tokenized after \\ 1385 1386 # query using character 1387 _, matches = complete(line_buffer="d['a\u05d0") 1388 self.assertIn("a\u05d0", matches) 1389 1390 with greedy_completion(): 1391 # query using escape 1392 _, matches = complete(line_buffer="d['a\\u05d0") 1393 self.assertIn("d['a\\u05d0']", matches) # tokenized after \\ 1394 1395 # query using character 1396 _, matches = complete(line_buffer="d['a\u05d0") 1397 self.assertIn("d['a\u05d0']", matches) 1398 1399 @dec.skip_without("numpy") 1400 def test_struct_array_key_completion(self): 1401 """Test dict key completion applies to numpy struct arrays""" 1402 import numpy 1403 1404 ip = get_ipython() 1405 complete = ip.Completer.complete 1406 ip.user_ns["d"] = numpy.array([], dtype=[("hello", "f"), ("world", "f")]) 1407 _, matches = complete(line_buffer="d['") 1408 self.assertIn("hello", matches) 1409 self.assertIn("world", matches) 1410 # complete on the numpy struct itself 1411 dt = numpy.dtype( 1412 [("my_head", [("my_dt", ">u4"), ("my_df", ">u4")]), ("my_data", ">f4", 5)] 1413 ) 1414 x = numpy.zeros(2, dtype=dt) 1415 ip.user_ns["d"] = x[1] 1416 _, matches = complete(line_buffer="d['") 1417 self.assertIn("my_head", matches) 1418 self.assertIn("my_data", matches) 1419 1420 def completes_on_nested(): 1421 ip.user_ns["d"] = numpy.zeros(2, dtype=dt) 1422 _, matches = complete(line_buffer="d[1]['my_head']['") 1423 self.assertTrue(any(["my_dt" in m for m in matches])) 1424 self.assertTrue(any(["my_df" in m for m in matches])) 1425 # complete on a nested level 1426 with greedy_completion(): 1427 completes_on_nested() 1428 1429 with evaluation_policy("limited"): 1430 completes_on_nested() 1431 1432 with evaluation_policy("minimal"): 1433 with pytest.raises(AssertionError): 1434 completes_on_nested() 1435 1436 @dec.skip_without("pandas") 1437 def test_dataframe_key_completion(self): 1438 """Test dict key completion applies to pandas DataFrames""" 1439 import pandas 1440 1441 ip = get_ipython() 1442 complete = ip.Completer.complete 1443 ip.user_ns["d"] = pandas.DataFrame({"hello": [1], "world": [2]}) 1444 _, matches = complete(line_buffer="d['") 1445 self.assertIn("hello", matches) 1446 self.assertIn("world", matches) 1447 _, matches = complete(line_buffer="d.loc[:, '") 1448 self.assertIn("hello", matches) 1449 self.assertIn("world", matches) 1450 _, matches = complete(line_buffer="d.loc[1:, '") 1451 self.assertIn("hello", matches) 1452 _, matches = complete(line_buffer="d.loc[1:1, '") 1453 self.assertIn("hello", matches) 1454 _, matches = complete(line_buffer="d.loc[1:1:-1, '") 1455 self.assertIn("hello", matches) 1456 _, matches = complete(line_buffer="d.loc[::, '") 1457 self.assertIn("hello", matches) 1458 1459 def test_dict_key_completion_invalids(self): 1460 """Smoke test cases dict key completion can't handle""" 1461 ip = get_ipython() 1462 complete = ip.Completer.complete 1463 1464 ip.user_ns["no_getitem"] = None 1465 ip.user_ns["no_keys"] = [] 1466 ip.user_ns["cant_call_keys"] = dict 1467 ip.user_ns["empty"] = {} 1468 ip.user_ns["d"] = {"abc": 5} 1469 1470 _, matches = complete(line_buffer="no_getitem['") 1471 _, matches = complete(line_buffer="no_keys['") 1472 _, matches = complete(line_buffer="cant_call_keys['") 1473 _, matches = complete(line_buffer="empty['") 1474 _, matches = complete(line_buffer="name_error['") 1475 _, matches = complete(line_buffer="d['\\") # incomplete escape 1476 1477 def test_object_key_completion(self): 1478 ip = get_ipython() 1479 ip.user_ns["key_completable"] = KeyCompletable(["qwerty", "qwick"]) 1480 1481 _, matches = ip.Completer.complete(line_buffer="key_completable['qw") 1482 self.assertIn("qwerty", matches) 1483 self.assertIn("qwick", matches) 1484 1485 def test_class_key_completion(self): 1486 ip = get_ipython() 1487 NamedInstanceClass("qwerty") 1488 NamedInstanceClass("qwick") 1489 ip.user_ns["named_instance_class"] = NamedInstanceClass 1490 1491 _, matches = ip.Completer.complete(line_buffer="named_instance_class['qw") 1492 self.assertIn("qwerty", matches) 1493 self.assertIn("qwick", matches) 1494 1495 def test_tryimport(self): 1496 """ 1497 Test that try-import don't crash on trailing dot, and import modules before 1498 """ 1499 from IPython.core.completerlib import try_import 1500 1501 assert try_import("IPython.") 1502 1503 def test_aimport_module_completer(self): 1504 ip = get_ipython() 1505 _, matches = ip.complete("i", "%aimport i") 1506 self.assertIn("io", matches) 1507 self.assertNotIn("int", matches) 1508 1509 def test_nested_import_module_completer(self): 1510 ip = get_ipython() 1511 _, matches = ip.complete(None, "import IPython.co", 17) 1512 self.assertIn("IPython.core", matches) 1513 self.assertNotIn("import IPython.core", matches) 1514 self.assertNotIn("IPython.display", matches) 1515 1516 def test_import_module_completer(self): 1517 ip = get_ipython() 1518 _, matches = ip.complete("i", "import i") 1519 self.assertIn("io", matches) 1520 self.assertNotIn("int", matches) 1521 1522 def test_from_module_completer(self): 1523 ip = get_ipython() 1524 _, matches = ip.complete("B", "from io import B", 16) 1525 self.assertIn("BytesIO", matches) 1526 self.assertNotIn("BaseException", matches) 1527 1528 def test_snake_case_completion(self): 1529 ip = get_ipython() 1530 ip.Completer.use_jedi = False 1531 ip.user_ns["some_three"] = 3 1532 ip.user_ns["some_four"] = 4 1533 _, matches = ip.complete("s_", "print(s_f") 1534 self.assertIn("some_three", matches) 1535 self.assertIn("some_four", matches) 1536 1537 def test_mix_terms(self): 1538 ip = get_ipython() 1539 from textwrap import dedent 1540 1541 ip.Completer.use_jedi = False 1542 ip.ex( 1543 dedent( 1544 """ 1545 class Test: 1546 def meth(self, meth_arg1): 1547 print("meth") 1548 1549 def meth_1(self, meth1_arg1, meth1_arg2): 1550 print("meth1") 1551 1552 def meth_2(self, meth2_arg1, meth2_arg2): 1553 print("meth2") 1554 test = Test() 1555 """ 1556 ) 1557 ) 1558 _, matches = ip.complete(None, "test.meth(") 1559 self.assertIn("meth_arg1=", matches) 1560 self.assertNotIn("meth2_arg1=", matches) 1561 1562 def test_percent_symbol_restrict_to_magic_completions(self): 1563 ip = get_ipython() 1564 completer = ip.Completer 1565 text = "%a" 1566 1567 with provisionalcompleter(): 1568 completer.use_jedi = True 1569 completions = completer.completions(text, len(text)) 1570 for c in completions: 1571 self.assertEqual(c.text[0], "%") 1572 1573 def test_fwd_unicode_restricts(self): 1574 ip = get_ipython() 1575 completer = ip.Completer 1576 text = "\\ROMAN NUMERAL FIVE" 1577 1578 with provisionalcompleter(): 1579 completer.use_jedi = True 1580 completions = [ 1581 completion.text for completion in completer.completions(text, len(text)) 1582 ] 1583 self.assertEqual(completions, ["\u2164"]) 1584 1585 def test_dict_key_restrict_to_dicts(self): 1586 """Test that dict key suppresses non-dict completion items""" 1587 ip = get_ipython() 1588 c = ip.Completer 1589 d = {"abc": None} 1590 ip.user_ns["d"] = d 1591 1592 text = 'd["a' 1593 1594 def _(): 1595 with provisionalcompleter(): 1596 c.use_jedi = True 1597 return [ 1598 completion.text for completion in c.completions(text, len(text)) 1599 ] 1600 1601 completions = _() 1602 self.assertEqual(completions, ["abc"]) 1603 1604 # check that it can be disabled in granular manner: 1605 cfg = Config() 1606 cfg.IPCompleter.suppress_competing_matchers = { 1607 "IPCompleter.dict_key_matcher": False 1608 } 1609 c.update_config(cfg) 1610 1611 completions = _() 1612 self.assertIn("abc", completions) 1613 self.assertGreater(len(completions), 1) 1614 1615 def test_matcher_suppression(self): 1616 @completion_matcher(identifier="a_matcher") 1617 def a_matcher(text): 1618 return ["completion_a"] 1619 1620 @completion_matcher(identifier="b_matcher", api_version=2) 1621 def b_matcher(context: CompletionContext): 1622 text = context.token 1623 result = {"completions": [SimpleCompletion("completion_b")]} 1624 1625 if text == "suppress c": 1626 result["suppress"] = {"c_matcher"} 1627 1628 if text.startswith("suppress all"): 1629 result["suppress"] = True 1630 if text == "suppress all but c": 1631 result["do_not_suppress"] = {"c_matcher"} 1632 if text == "suppress all but a": 1633 result["do_not_suppress"] = {"a_matcher"} 1634 1635 return result 1636 1637 @completion_matcher(identifier="c_matcher") 1638 def c_matcher(text): 1639 return ["completion_c"] 1640 1641 with custom_matchers([a_matcher, b_matcher, c_matcher]): 1642 ip = get_ipython() 1643 c = ip.Completer 1644 1645 def _(text, expected): 1646 c.use_jedi = False 1647 s, matches = c.complete(text) 1648 self.assertEqual(expected, matches) 1649 1650 _("do not suppress", ["completion_a", "completion_b", "completion_c"]) 1651 _("suppress all", ["completion_b"]) 1652 _("suppress all but a", ["completion_a", "completion_b"]) 1653 _("suppress all but c", ["completion_b", "completion_c"]) 1654 1655 def configure(suppression_config): 1656 cfg = Config() 1657 cfg.IPCompleter.suppress_competing_matchers = suppression_config 1658 c.update_config(cfg) 1659 1660 # test that configuration takes priority over the run-time decisions 1661 1662 configure(False) 1663 _("suppress all", ["completion_a", "completion_b", "completion_c"]) 1664 1665 configure({"b_matcher": False}) 1666 _("suppress all", ["completion_a", "completion_b", "completion_c"]) 1667 1668 configure({"a_matcher": False}) 1669 _("suppress all", ["completion_b"]) 1670 1671 configure({"b_matcher": True}) 1672 _("do not suppress", ["completion_b"]) 1673 1674 configure(True) 1675 _("do not suppress", ["completion_a"]) 1676 1677 def test_matcher_suppression_with_iterator(self): 1678 @completion_matcher(identifier="matcher_returning_iterator") 1679 def matcher_returning_iterator(text): 1680 return iter(["completion_iter"]) 1681 1682 @completion_matcher(identifier="matcher_returning_list") 1683 def matcher_returning_list(text): 1684 return ["completion_list"] 1685 1686 with custom_matchers([matcher_returning_iterator, matcher_returning_list]): 1687 ip = get_ipython() 1688 c = ip.Completer 1689 1690 def _(text, expected): 1691 c.use_jedi = False 1692 s, matches = c.complete(text) 1693 self.assertEqual(expected, matches) 1694 1695 def configure(suppression_config): 1696 cfg = Config() 1697 cfg.IPCompleter.suppress_competing_matchers = suppression_config 1698 c.update_config(cfg) 1699 1700 configure(False) 1701 _("---", ["completion_iter", "completion_list"]) 1702 1703 configure(True) 1704 _("---", ["completion_iter"]) 1705 1706 configure(None) 1707 _("--", ["completion_iter", "completion_list"]) 1708 1709 @pytest.mark.xfail( 1710 sys.version_info.releaselevel in ("alpha",), 1711 reason="Parso does not yet parse 3.13", 1712 ) 1713 def test_matcher_suppression_with_jedi(self): 1714 ip = get_ipython() 1715 c = ip.Completer 1716 c.use_jedi = True 1717 1718 def configure(suppression_config): 1719 cfg = Config() 1720 cfg.IPCompleter.suppress_competing_matchers = suppression_config 1721 c.update_config(cfg) 1722 1723 def _(): 1724 with provisionalcompleter(): 1725 matches = [completion.text for completion in c.completions("dict.", 5)] 1726 self.assertIn("keys", matches) 1727 1728 configure(False) 1729 _() 1730 1731 configure(True) 1732 _() 1733 1734 configure(None) 1735 _() 1736 1737 def test_matcher_disabling(self): 1738 @completion_matcher(identifier="a_matcher") 1739 def a_matcher(text): 1740 return ["completion_a"] 1741 1742 @completion_matcher(identifier="b_matcher") 1743 def b_matcher(text): 1744 return ["completion_b"] 1745 1746 def _(expected): 1747 s, matches = c.complete("completion_") 1748 self.assertEqual(expected, matches) 1749 1750 with custom_matchers([a_matcher, b_matcher]): 1751 ip = get_ipython() 1752 c = ip.Completer 1753 1754 _(["completion_a", "completion_b"]) 1755 1756 cfg = Config() 1757 cfg.IPCompleter.disable_matchers = ["b_matcher"] 1758 c.update_config(cfg) 1759 1760 _(["completion_a"]) 1761 1762 cfg.IPCompleter.disable_matchers = [] 1763 c.update_config(cfg) 1764 1765 def test_matcher_priority(self): 1766 @completion_matcher(identifier="a_matcher", priority=0, api_version=2) 1767 def a_matcher(text): 1768 return {"completions": [SimpleCompletion("completion_a")], "suppress": True} 1769 1770 @completion_matcher(identifier="b_matcher", priority=2, api_version=2) 1771 def b_matcher(text): 1772 return {"completions": [SimpleCompletion("completion_b")], "suppress": True} 1773 1774 def _(expected): 1775 s, matches = c.complete("completion_") 1776 self.assertEqual(expected, matches) 1777 1778 with custom_matchers([a_matcher, b_matcher]): 1779 ip = get_ipython() 1780 c = ip.Completer 1781 1782 _(["completion_b"]) 1783 a_matcher.matcher_priority = 3 1784 _(["completion_a"]) 1785 1786 1787 @pytest.mark.parametrize( 1788 "line,expected", 1789 [ 1790 # Basic test cases 1791 ("np.", "attribute"), 1792 ("np.ran", "attribute"), 1793 ("np.random.rand(np.random.ran", "attribute"), 1794 ("np.random.rand(n", "global"), 1795 ("d['k.e.y.'](ran", "global"), 1796 ("d[0].k", "attribute"), 1797 ("a = { 'a': np.ran", "attribute"), 1798 ("n", "global"), 1799 ("", "global"), 1800 # Dots in string literals 1801 ('some_var = "this is a string with a dot.', "global"), 1802 ("text = 'another string with a dot.", "global"), 1803 ('f"greeting {user.na', "attribute"), # Cursor in f-string expression 1804 ('t"welcome {guest.na', "attribute"), # Cursor in t-string expression 1805 ('f"hello {name} worl', "global"), # Cursor in f-string outside expression 1806 ('f"hello {{a.', "global"), 1807 ('f"hello {{{a.', "attribute"), 1808 # Backslash escapes in strings 1809 ('var = "string with \\"escaped quote and a dot.', "global"), 1810 ("escaped = 'single \\'quote\\' with a dot.", "global"), 1811 # Multi-line strings 1812 ('multi = """This is line one\nwith a dot.', "global"), 1813 ("multi_single = '''Another\nmulti-line\nwith a dot.", "global"), 1814 # Inline comments 1815 ("x = 5 # This is a comment", "global"), 1816 ("y = obj.method() # Comment after dot.method", "global"), 1817 # Hash symbol within string literals should not be treated as comments 1818 ("d['#'] = np.", "attribute"), 1819 # Nested parentheses with dots 1820 ("complex_expr = (func((obj.method(param.attr", "attribute"), 1821 ("multiple_nesting = {key: [value.attr", "attribute"), 1822 # Numbers 1823 ("3.", "global"), 1824 ("3.14", "global"), 1825 ("-42.14", "global"), 1826 ("x = func(3.14", "global"), 1827 ("x = func(a3.", "attribute"), 1828 ("x = func(a3.12", "global"), 1829 ("3.1.", "attribute"), 1830 ("-3.1.", "attribute"), 1831 ("(3).", "attribute"), 1832 # Additional cases 1833 ("", "global"), 1834 ('str_with_code = "x.attr', "global"), 1835 ('f"formatted {obj.attr', "attribute"), 1836 ('f"formatted {obj.attr}', "global"), 1837 ("dict_with_dots = {'key.with.dots': value.attr", "attribute"), 1838 ("d[f'{a}']['{a.", "global"), 1839 ], 1840 ) 1841 def test_completion_context(line, expected): 1842 """Test completion context""" 1843 ip = get_ipython() 1844 get_context = ip.Completer._determine_completion_context 1845 result = get_context(line) 1846 assert result.value == expected, f"Failed on input: '{line}'" 1847 1848 1849 @pytest.mark.xfail(reason="Completion context not yet supported") 1850 @pytest.mark.parametrize( 1851 "line, expected", 1852 [ 1853 ("f'{f'a.", "global"), # Nested f-string 1854 ("3a.", "global"), # names starting with numbers or other symbols 1855 ("$).", "global"), # random things with dot at end 1856 ], 1857 ) 1858 def test_unsupported_completion_context(line, expected): 1859 """Test unsupported completion context""" 1860 ip = get_ipython() 1861 get_context = ip.Completer._determine_completion_context 1862 result = get_context(line) 1863 assert result.value == expected, f"Failed on input: '{line}'" 1864 1865 1866 @pytest.mark.parametrize( 1867 "setup,code,expected,not_expected", 1868 [ 1869 ('a="str"; b=1', "(a, b.", [".bit_count", ".conjugate"], [".count"]), 1870 ('a="str"; b=1', "(a, b).", [".count"], [".bit_count", ".capitalize"]), 1871 ('x="str"; y=1', "x = {1, y.", [".bit_count"], [".count"]), 1872 ('x="str"; y=1', "x = [1, y.", [".bit_count"], [".count"]), 1873 ('x="str"; y=1; fun=lambda x:x', "x = fun(1, y.", [".bit_count"], [".count"]), 1874 ], 1875 ) 1876 def test_misc_no_jedi_completions(setup, code, expected, not_expected): 1877 ip = get_ipython() 1878 c = ip.Completer 1879 ip.ex(setup) 1880 with provisionalcompleter(), jedi_status(False): 1881 matches = c.all_completions(code) 1882 assert set(expected) - set(matches) == set(), set(matches) 1883 assert set(matches).intersection(set(not_expected)) == set() 1884 1885 1886 @pytest.mark.parametrize( 1887 "code,expected", 1888 [ 1889 (" (a, b", "b"), 1890 ("(a, b", "b"), 1891 ("(a, b)", ""), # trim always start by trimming 1892 (" (a, b)", "(a, b)"), 1893 (" [a, b]", "[a, b]"), 1894 (" a, b", "b"), 1895 ("x = {1, y", "y"), 1896 ("x = [1, y", "y"), 1897 ("x = fun(1, y", "y"), 1898 ], 1899 ) 1900 def test_trim_expr(code, expected): 1901 c = get_ipython().Completer 1902 assert c._trim_expr(code) == expected 1903 1904 1905 @pytest.mark.parametrize( 1906 "input, expected", 1907 [ 1908 ["1.234", "1.234"], 1909 # should match signed numbers 1910 ["+1", "+1"], 1911 ["-1", "-1"], 1912 ["-1.0", "-1.0"], 1913 ["-1.", "-1."], 1914 ["+1.", "+1."], 1915 [".1", ".1"], 1916 # should not match non-numbers 1917 ["1..", None], 1918 ["..", None], 1919 [".1.", None], 1920 # should match after comma 1921 [",1", "1"], 1922 [", 1", "1"], 1923 [", .1", ".1"], 1924 [", +.1", "+.1"], 1925 # should not match after trailing spaces 1926 [".1 ", None], 1927 # some complex cases 1928 ["0b_0011_1111_0100_1110", "0b_0011_1111_0100_1110"], 1929 ["0xdeadbeef", "0xdeadbeef"], 1930 ["0b_1110_0101", "0b_1110_0101"], 1931 # should not match if in an operation 1932 ["1 + 1", None], 1933 [", 1 + 1", None], 1934 ], 1935 ) 1936 def test_match_numeric_literal_for_dict_key(input, expected): 1937 assert _match_number_in_dict_key_prefix(input) == expected 1938