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