##// END OF EJS Templates
Fix typos
Andrew Kreimer -
Show More
@@ -1,1769 +1,1769
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 importlib.metadata import version
14 14
15 15
16 16 from contextlib import contextmanager
17 17
18 18 from traitlets.config.loader import Config
19 19 from IPython import get_ipython
20 20 from IPython.core import completer
21 21 from IPython.utils.tempdir import TemporaryDirectory, TemporaryWorkingDirectory
22 22 from IPython.utils.generics import complete_object
23 23 from IPython.testing import decorators as dec
24 24
25 25 from IPython.core.completer import (
26 26 Completion,
27 27 provisionalcompleter,
28 28 match_dict_keys,
29 29 _deduplicate_completions,
30 30 _match_number_in_dict_key_prefix,
31 31 completion_matcher,
32 32 SimpleCompletion,
33 33 CompletionContext,
34 34 )
35 35
36 36 from packaging.version import parse
37 37
38 38
39 39 # -----------------------------------------------------------------------------
40 40 # Test functions
41 41 # -----------------------------------------------------------------------------
42 42
43 43
44 44 def recompute_unicode_ranges():
45 45 """
46 46 utility to recompute the largest unicode range without any characters
47 47
48 48 use to recompute the gap in the global _UNICODE_RANGES of completer.py
49 49 """
50 50 import itertools
51 51 import unicodedata
52 52
53 53 valid = []
54 54 for c in range(0, 0x10FFFF + 1):
55 55 try:
56 56 unicodedata.name(chr(c))
57 57 except ValueError:
58 58 continue
59 59 valid.append(c)
60 60
61 61 def ranges(i):
62 62 for a, b in itertools.groupby(enumerate(i), lambda pair: pair[1] - pair[0]):
63 63 b = list(b)
64 64 yield b[0][1], b[-1][1]
65 65
66 66 rg = list(ranges(valid))
67 67 lens = []
68 68 gap_lens = []
69 69 pstart, pstop = 0, 0
70 70 for start, stop in rg:
71 71 lens.append(stop - start)
72 72 gap_lens.append(
73 73 (
74 74 start - pstop,
75 75 hex(pstop + 1),
76 76 hex(start),
77 77 f"{round((start - pstop)/0xe01f0*100)}%",
78 78 )
79 79 )
80 80 pstart, pstop = start, stop
81 81
82 82 return sorted(gap_lens)[-1]
83 83
84 84
85 85 def test_unicode_range():
86 86 """
87 87 Test that the ranges we test for unicode names give the same number of
88 88 results than testing the full length.
89 89 """
90 90 from IPython.core.completer import _unicode_name_compute, _UNICODE_RANGES
91 91
92 92 expected_list = _unicode_name_compute([(0, 0x110000)])
93 93 test = _unicode_name_compute(_UNICODE_RANGES)
94 94 len_exp = len(expected_list)
95 95 len_test = len(test)
96 96
97 97 # do not inline the len() or on error pytest will try to print the 130 000 +
98 98 # elements.
99 99 message = None
100 100 if len_exp != len_test or len_exp > 131808:
101 101 size, start, stop, prct = recompute_unicode_ranges()
102 102 message = f"""_UNICODE_RANGES likely wrong and need updating. This is
103 103 likely due to a new release of Python. We've find that the biggest gap
104 104 in unicode characters has reduces in size to be {size} characters
105 105 ({prct}), from {start}, to {stop}. In completer.py likely update to
106 106
107 107 _UNICODE_RANGES = [(32, {start}), ({stop}, 0xe01f0)]
108 108
109 109 And update the assertion below to use
110 110
111 111 len_exp <= {len_exp}
112 112 """
113 113 assert len_exp == len_test, message
114 114
115 115 # fail if new unicode symbols have been added.
116 116 assert len_exp <= 143668, message
117 117
118 118
119 119 @contextmanager
120 120 def greedy_completion():
121 121 ip = get_ipython()
122 122 greedy_original = ip.Completer.greedy
123 123 try:
124 124 ip.Completer.greedy = True
125 125 yield
126 126 finally:
127 127 ip.Completer.greedy = greedy_original
128 128
129 129
130 130 @contextmanager
131 131 def evaluation_policy(evaluation: str):
132 132 ip = get_ipython()
133 133 evaluation_original = ip.Completer.evaluation
134 134 try:
135 135 ip.Completer.evaluation = evaluation
136 136 yield
137 137 finally:
138 138 ip.Completer.evaluation = evaluation_original
139 139
140 140
141 141 @contextmanager
142 142 def custom_matchers(matchers):
143 143 ip = get_ipython()
144 144 try:
145 145 ip.Completer.custom_matchers.extend(matchers)
146 146 yield
147 147 finally:
148 148 ip.Completer.custom_matchers.clear()
149 149
150 150
151 151 def test_protect_filename():
152 152 if sys.platform == "win32":
153 153 pairs = [
154 154 ("abc", "abc"),
155 155 (" abc", '" abc"'),
156 156 ("a bc", '"a bc"'),
157 157 ("a bc", '"a bc"'),
158 158 (" bc", '" bc"'),
159 159 ]
160 160 else:
161 161 pairs = [
162 162 ("abc", "abc"),
163 163 (" abc", r"\ abc"),
164 164 ("a bc", r"a\ bc"),
165 165 ("a bc", r"a\ \ bc"),
166 166 (" bc", r"\ \ bc"),
167 167 # On posix, we also protect parens and other special characters.
168 168 ("a(bc", r"a\(bc"),
169 169 ("a)bc", r"a\)bc"),
170 170 ("a( )bc", r"a\(\ \)bc"),
171 171 ("a[1]bc", r"a\[1\]bc"),
172 172 ("a{1}bc", r"a\{1\}bc"),
173 173 ("a#bc", r"a\#bc"),
174 174 ("a?bc", r"a\?bc"),
175 175 ("a=bc", r"a\=bc"),
176 176 ("a\\bc", r"a\\bc"),
177 177 ("a|bc", r"a\|bc"),
178 178 ("a;bc", r"a\;bc"),
179 179 ("a:bc", r"a\:bc"),
180 180 ("a'bc", r"a\'bc"),
181 181 ("a*bc", r"a\*bc"),
182 182 ('a"bc', r"a\"bc"),
183 183 ("a^bc", r"a\^bc"),
184 184 ("a&bc", r"a\&bc"),
185 185 ]
186 186 # run the actual tests
187 187 for s1, s2 in pairs:
188 188 s1p = completer.protect_filename(s1)
189 189 assert s1p == s2
190 190
191 191
192 192 def check_line_split(splitter, test_specs):
193 193 for part1, part2, split in test_specs:
194 194 cursor_pos = len(part1)
195 195 line = part1 + part2
196 196 out = splitter.split_line(line, cursor_pos)
197 197 assert out == split
198 198
199 199 def test_line_split():
200 200 """Basic line splitter test with default specs."""
201 201 sp = completer.CompletionSplitter()
202 202 # The format of the test specs is: part1, part2, expected answer. Parts 1
203 203 # and 2 are joined into the 'line' sent to the splitter, as if the cursor
204 204 # was at the end of part1. So an empty part2 represents someone hitting
205 205 # tab at the end of the line, the most common case.
206 206 t = [
207 ("run some/scrip", "", "some/scrip"),
207 ("run some/script", "", "some/script"),
208 208 ("run scripts/er", "ror.py foo", "scripts/er"),
209 209 ("echo $HOM", "", "HOM"),
210 210 ("print sys.pa", "", "sys.pa"),
211 211 ("print(sys.pa", "", "sys.pa"),
212 212 ("execfile('scripts/er", "", "scripts/er"),
213 213 ("a[x.", "", "x."),
214 214 ("a[x.", "y", "x."),
215 215 ('cd "some_file/', "", "some_file/"),
216 216 ]
217 217 check_line_split(sp, t)
218 218 # Ensure splitting works OK with unicode by re-running the tests with
219 219 # all inputs turned into unicode
220 220 check_line_split(sp, [map(str, p) for p in t])
221 221
222 222
223 223 class NamedInstanceClass:
224 224 instances = {}
225 225
226 226 def __init__(self, name):
227 227 self.instances[name] = self
228 228
229 229 @classmethod
230 230 def _ipython_key_completions_(cls):
231 231 return cls.instances.keys()
232 232
233 233
234 234 class KeyCompletable:
235 235 def __init__(self, things=()):
236 236 self.things = things
237 237
238 238 def _ipython_key_completions_(self):
239 239 return list(self.things)
240 240
241 241
242 242 class TestCompleter(unittest.TestCase):
243 243 def setUp(self):
244 244 """
245 245 We want to silence all PendingDeprecationWarning when testing the completer
246 246 """
247 247 self._assertwarns = self.assertWarns(PendingDeprecationWarning)
248 248 self._assertwarns.__enter__()
249 249
250 250 def tearDown(self):
251 251 try:
252 252 self._assertwarns.__exit__(None, None, None)
253 253 except AssertionError:
254 254 pass
255 255
256 256 def test_custom_completion_error(self):
257 257 """Test that errors from custom attribute completers are silenced."""
258 258 ip = get_ipython()
259 259
260 260 class A:
261 261 pass
262 262
263 263 ip.user_ns["x"] = A()
264 264
265 265 @complete_object.register(A)
266 266 def complete_A(a, existing_completions):
267 267 raise TypeError("this should be silenced")
268 268
269 269 ip.complete("x.")
270 270
271 271 def test_custom_completion_ordering(self):
272 272 """Test that errors from custom attribute completers are silenced."""
273 273 ip = get_ipython()
274 274
275 275 _, matches = ip.complete('in')
276 276 assert matches.index('input') < matches.index('int')
277 277
278 278 def complete_example(a):
279 279 return ['example2', 'example1']
280 280
281 281 ip.Completer.custom_completers.add_re('ex*', complete_example)
282 282 _, matches = ip.complete('ex')
283 283 assert matches.index('example2') < matches.index('example1')
284 284
285 285 def test_unicode_completions(self):
286 286 ip = get_ipython()
287 287 # Some strings that trigger different types of completion. Check them both
288 288 # in str and unicode forms
289 289 s = ["ru", "%ru", "cd /", "floa", "float(x)/"]
290 290 for t in s + list(map(str, s)):
291 291 # We don't need to check exact completion values (they may change
292 292 # depending on the state of the namespace, but at least no exceptions
293 293 # should be thrown and the return value should be a pair of text, list
294 294 # values.
295 295 text, matches = ip.complete(t)
296 296 self.assertIsInstance(text, str)
297 297 self.assertIsInstance(matches, list)
298 298
299 299 def test_latex_completions(self):
300 300 from IPython.core.latex_symbols import latex_symbols
301 301 import random
302 302
303 303 ip = get_ipython()
304 304 # Test some random unicode symbols
305 305 keys = random.sample(sorted(latex_symbols), 10)
306 306 for k in keys:
307 307 text, matches = ip.complete(k)
308 308 self.assertEqual(text, k)
309 309 self.assertEqual(matches, [latex_symbols[k]])
310 310 # Test a more complex line
311 311 text, matches = ip.complete("print(\\alpha")
312 312 self.assertEqual(text, "\\alpha")
313 313 self.assertEqual(matches[0], latex_symbols["\\alpha"])
314 314 # Test multiple matching latex symbols
315 315 text, matches = ip.complete("\\al")
316 316 self.assertIn("\\alpha", matches)
317 317 self.assertIn("\\aleph", matches)
318 318
319 319 def test_latex_no_results(self):
320 320 """
321 321 forward latex should really return nothing in either field if nothing is found.
322 322 """
323 323 ip = get_ipython()
324 324 text, matches = ip.Completer.latex_matches("\\really_i_should_match_nothing")
325 325 self.assertEqual(text, "")
326 326 self.assertEqual(matches, ())
327 327
328 328 def test_back_latex_completion(self):
329 329 ip = get_ipython()
330 330
331 331 # do not return more than 1 matches for \beta, only the latex one.
332 332 name, matches = ip.complete("\\Ξ²")
333 333 self.assertEqual(matches, ["\\beta"])
334 334
335 335 def test_back_unicode_completion(self):
336 336 ip = get_ipython()
337 337
338 338 name, matches = ip.complete("\\β…€")
339 339 self.assertEqual(matches, ["\\ROMAN NUMERAL FIVE"])
340 340
341 341 def test_forward_unicode_completion(self):
342 342 ip = get_ipython()
343 343
344 344 name, matches = ip.complete("\\ROMAN NUMERAL FIVE")
345 345 self.assertEqual(matches, ["β…€"]) # This is not a V
346 346 self.assertEqual(matches, ["\u2164"]) # same as above but explicit.
347 347
348 348 def test_delim_setting(self):
349 349 sp = completer.CompletionSplitter()
350 350 sp.delims = " "
351 351 self.assertEqual(sp.delims, " ")
352 352 self.assertEqual(sp._delim_expr, r"[\ ]")
353 353
354 354 def test_spaces(self):
355 355 """Test with only spaces as split chars."""
356 356 sp = completer.CompletionSplitter()
357 357 sp.delims = " "
358 358 t = [("foo", "", "foo"), ("run foo", "", "foo"), ("run foo", "bar", "foo")]
359 359 check_line_split(sp, t)
360 360
361 361 def test_has_open_quotes1(self):
362 362 for s in ["'", "'''", "'hi' '"]:
363 363 self.assertEqual(completer.has_open_quotes(s), "'")
364 364
365 365 def test_has_open_quotes2(self):
366 366 for s in ['"', '"""', '"hi" "']:
367 367 self.assertEqual(completer.has_open_quotes(s), '"')
368 368
369 369 def test_has_open_quotes3(self):
370 370 for s in ["''", "''' '''", "'hi' 'ipython'"]:
371 371 self.assertFalse(completer.has_open_quotes(s))
372 372
373 373 def test_has_open_quotes4(self):
374 374 for s in ['""', '""" """', '"hi" "ipython"']:
375 375 self.assertFalse(completer.has_open_quotes(s))
376 376
377 377 @pytest.mark.xfail(
378 378 sys.platform == "win32", reason="abspath completions fail on Windows"
379 379 )
380 380 def test_abspath_file_completions(self):
381 381 ip = get_ipython()
382 382 with TemporaryDirectory() as tmpdir:
383 383 prefix = os.path.join(tmpdir, "foo")
384 384 suffixes = ["1", "2"]
385 385 names = [prefix + s for s in suffixes]
386 386 for n in names:
387 387 open(n, "w", encoding="utf-8").close()
388 388
389 389 # Check simple completion
390 390 c = ip.complete(prefix)[1]
391 391 self.assertEqual(c, names)
392 392
393 393 # Now check with a function call
394 394 cmd = 'a = f("%s' % prefix
395 395 c = ip.complete(prefix, cmd)[1]
396 396 comp = [prefix + s for s in suffixes]
397 397 self.assertEqual(c, comp)
398 398
399 399 def test_local_file_completions(self):
400 400 ip = get_ipython()
401 401 with TemporaryWorkingDirectory():
402 402 prefix = "./foo"
403 403 suffixes = ["1", "2"]
404 404 names = [prefix + s for s in suffixes]
405 405 for n in names:
406 406 open(n, "w", encoding="utf-8").close()
407 407
408 408 # Check simple completion
409 409 c = ip.complete(prefix)[1]
410 410 self.assertEqual(c, names)
411 411
412 412 # Now check with a function call
413 413 cmd = 'a = f("%s' % prefix
414 414 c = ip.complete(prefix, cmd)[1]
415 415 comp = {prefix + s for s in suffixes}
416 416 self.assertTrue(comp.issubset(set(c)))
417 417
418 418 def test_quoted_file_completions(self):
419 419 ip = get_ipython()
420 420
421 421 def _(text):
422 422 return ip.Completer._complete(
423 423 cursor_line=0, cursor_pos=len(text), full_text=text
424 424 )["IPCompleter.file_matcher"]["completions"]
425 425
426 426 with TemporaryWorkingDirectory():
427 427 name = "foo'bar"
428 428 open(name, "w", encoding="utf-8").close()
429 429
430 430 # Don't escape Windows
431 431 escaped = name if sys.platform == "win32" else "foo\\'bar"
432 432
433 433 # Single quote matches embedded single quote
434 434 c = _("open('foo")[0]
435 435 self.assertEqual(c.text, escaped)
436 436
437 437 # Double quote requires no escape
438 438 c = _('open("foo')[0]
439 439 self.assertEqual(c.text, name)
440 440
441 441 # No quote requires an escape
442 442 c = _("%ls foo")[0]
443 443 self.assertEqual(c.text, escaped)
444 444
445 445 @pytest.mark.xfail(
446 446 sys.version_info.releaselevel in ("alpha",),
447 447 reason="Parso does not yet parse 3.13",
448 448 )
449 449 def test_all_completions_dups(self):
450 450 """
451 451 Make sure the output of `IPCompleter.all_completions` does not have
452 452 duplicated prefixes.
453 453 """
454 454 ip = get_ipython()
455 455 c = ip.Completer
456 456 ip.ex("class TestClass():\n\ta=1\n\ta1=2")
457 457 for jedi_status in [True, False]:
458 458 with provisionalcompleter():
459 459 ip.Completer.use_jedi = jedi_status
460 460 matches = c.all_completions("TestCl")
461 461 assert matches == ["TestClass"], (jedi_status, matches)
462 462 matches = c.all_completions("TestClass.")
463 463 assert len(matches) > 2, (jedi_status, matches)
464 464 matches = c.all_completions("TestClass.a")
465 465 if jedi_status:
466 466 assert matches == ["TestClass.a", "TestClass.a1"], jedi_status
467 467 else:
468 468 assert matches == [".a", ".a1"], jedi_status
469 469
470 470 @pytest.mark.xfail(
471 471 sys.version_info.releaselevel in ("alpha",),
472 472 reason="Parso does not yet parse 3.13",
473 473 )
474 474 def test_jedi(self):
475 475 """
476 476 A couple of issue we had with Jedi
477 477 """
478 478 ip = get_ipython()
479 479
480 480 def _test_complete(reason, s, comp, start=None, end=None):
481 481 l = len(s)
482 482 start = start if start is not None else l
483 483 end = end if end is not None else l
484 484 with provisionalcompleter():
485 485 ip.Completer.use_jedi = True
486 486 completions = set(ip.Completer.completions(s, l))
487 487 ip.Completer.use_jedi = False
488 488 assert Completion(start, end, comp) in completions, reason
489 489
490 490 def _test_not_complete(reason, s, comp):
491 491 l = len(s)
492 492 with provisionalcompleter():
493 493 ip.Completer.use_jedi = True
494 494 completions = set(ip.Completer.completions(s, l))
495 495 ip.Completer.use_jedi = False
496 496 assert Completion(l, l, comp) not in completions, reason
497 497
498 498 import jedi
499 499
500 500 jedi_version = tuple(int(i) for i in jedi.__version__.split(".")[:3])
501 501 if jedi_version > (0, 10):
502 502 _test_complete("jedi >0.9 should complete and not crash", "a=1;a.", "real")
503 503 _test_complete("can infer first argument", 'a=(1,"foo");a[0].', "real")
504 504 _test_complete("can infer second argument", 'a=(1,"foo");a[1].', "capitalize")
505 505 _test_complete("cover duplicate completions", "im", "import", 0, 2)
506 506
507 507 _test_not_complete("does not mix types", 'a=(1,"foo");a[0].', "capitalize")
508 508
509 509 @pytest.mark.xfail(
510 510 sys.version_info.releaselevel in ("alpha",),
511 511 reason="Parso does not yet parse 3.13",
512 512 )
513 513 def test_completion_have_signature(self):
514 514 """
515 515 Lets make sure jedi is capable of pulling out the signature of the function we are completing.
516 516 """
517 517 ip = get_ipython()
518 518 with provisionalcompleter():
519 519 ip.Completer.use_jedi = True
520 520 completions = ip.Completer.completions("ope", 3)
521 521 c = next(completions) # should be `open`
522 522 ip.Completer.use_jedi = False
523 523 assert "file" in c.signature, "Signature of function was not found by completer"
524 524 assert (
525 525 "encoding" in c.signature
526 526 ), "Signature of function was not found by completer"
527 527
528 528 @pytest.mark.xfail(
529 529 sys.version_info.releaselevel in ("alpha",),
530 530 reason="Parso does not yet parse 3.13",
531 531 )
532 532 def test_completions_have_type(self):
533 533 """
534 534 Lets make sure matchers provide completion type.
535 535 """
536 536 ip = get_ipython()
537 537 with provisionalcompleter():
538 538 ip.Completer.use_jedi = False
539 539 completions = ip.Completer.completions("%tim", 3)
540 540 c = next(completions) # should be `%time` or similar
541 541 assert c.type == "magic", "Type of magic was not assigned by completer"
542 542
543 543 @pytest.mark.xfail(
544 544 parse(version("jedi")) <= parse("0.18.0"),
545 545 reason="Known failure on jedi<=0.18.0",
546 546 strict=True,
547 547 )
548 548 def test_deduplicate_completions(self):
549 549 """
550 550 Test that completions are correctly deduplicated (even if ranges are not the same)
551 551 """
552 552 ip = get_ipython()
553 553 ip.ex(
554 554 textwrap.dedent(
555 555 """
556 556 class Z:
557 557 zoo = 1
558 558 """
559 559 )
560 560 )
561 561 with provisionalcompleter():
562 562 ip.Completer.use_jedi = True
563 563 l = list(
564 564 _deduplicate_completions("Z.z", ip.Completer.completions("Z.z", 3))
565 565 )
566 566 ip.Completer.use_jedi = False
567 567
568 568 assert len(l) == 1, "Completions (Z.z<tab>) correctly deduplicate: %s " % l
569 569 assert l[0].text == "zoo" # and not `it.accumulate`
570 570
571 571 @pytest.mark.xfail(
572 572 sys.version_info.releaselevel in ("alpha",),
573 573 reason="Parso does not yet parse 3.13",
574 574 )
575 575 def test_greedy_completions(self):
576 576 """
577 577 Test the capability of the Greedy completer.
578 578
579 579 Most of the test here does not really show off the greedy completer, for proof
580 580 each of the text below now pass with Jedi. The greedy completer is capable of more.
581 581
582 582 See the :any:`test_dict_key_completion_contexts`
583 583
584 584 """
585 585 ip = get_ipython()
586 586 ip.ex("a=list(range(5))")
587 587 ip.ex("d = {'a b': str}")
588 588 _, c = ip.complete(".", line="a[0].")
589 589 self.assertFalse(".real" in c, "Shouldn't have completed on a[0]: %s" % c)
590 590
591 591 def _(line, cursor_pos, expect, message, completion):
592 592 with greedy_completion(), provisionalcompleter():
593 593 ip.Completer.use_jedi = False
594 594 _, c = ip.complete(".", line=line, cursor_pos=cursor_pos)
595 595 self.assertIn(expect, c, message % c)
596 596
597 597 ip.Completer.use_jedi = True
598 598 with provisionalcompleter():
599 599 completions = ip.Completer.completions(line, cursor_pos)
600 600 self.assertIn(completion, list(completions))
601 601
602 602 with provisionalcompleter():
603 603 _(
604 604 "a[0].",
605 605 5,
606 606 ".real",
607 607 "Should have completed on a[0].: %s",
608 608 Completion(5, 5, "real"),
609 609 )
610 610 _(
611 611 "a[0].r",
612 612 6,
613 613 ".real",
614 614 "Should have completed on a[0].r: %s",
615 615 Completion(5, 6, "real"),
616 616 )
617 617
618 618 _(
619 619 "a[0].from_",
620 620 10,
621 621 ".from_bytes",
622 622 "Should have completed on a[0].from_: %s",
623 623 Completion(5, 10, "from_bytes"),
624 624 )
625 625 _(
626 626 "assert str.star",
627 627 14,
628 628 ".startswith",
629 629 "Should have completed on `assert str.star`: %s",
630 630 Completion(11, 14, "startswith"),
631 631 )
632 632 _(
633 633 "d['a b'].str",
634 634 12,
635 635 ".strip",
636 636 "Should have completed on `d['a b'].str`: %s",
637 637 Completion(9, 12, "strip"),
638 638 )
639 639 _(
640 640 "a.app",
641 641 4,
642 642 ".append",
643 643 "Should have completed on `a.app`: %s",
644 644 Completion(2, 4, "append"),
645 645 )
646 646
647 647 def test_omit__names(self):
648 648 # also happens to test IPCompleter as a configurable
649 649 ip = get_ipython()
650 650 ip._hidden_attr = 1
651 651 ip._x = {}
652 652 c = ip.Completer
653 653 ip.ex("ip=get_ipython()")
654 654 cfg = Config()
655 655 cfg.IPCompleter.omit__names = 0
656 656 c.update_config(cfg)
657 657 with provisionalcompleter():
658 658 c.use_jedi = False
659 659 s, matches = c.complete("ip.")
660 660 self.assertIn(".__str__", matches)
661 661 self.assertIn("._hidden_attr", matches)
662 662
663 663 # c.use_jedi = True
664 664 # completions = set(c.completions('ip.', 3))
665 665 # self.assertIn(Completion(3, 3, '__str__'), completions)
666 666 # self.assertIn(Completion(3,3, "_hidden_attr"), completions)
667 667
668 668 cfg = Config()
669 669 cfg.IPCompleter.omit__names = 1
670 670 c.update_config(cfg)
671 671 with provisionalcompleter():
672 672 c.use_jedi = False
673 673 s, matches = c.complete("ip.")
674 674 self.assertNotIn(".__str__", matches)
675 675 # self.assertIn('ip._hidden_attr', matches)
676 676
677 677 # c.use_jedi = True
678 678 # completions = set(c.completions('ip.', 3))
679 679 # self.assertNotIn(Completion(3,3,'__str__'), completions)
680 680 # self.assertIn(Completion(3,3, "_hidden_attr"), completions)
681 681
682 682 cfg = Config()
683 683 cfg.IPCompleter.omit__names = 2
684 684 c.update_config(cfg)
685 685 with provisionalcompleter():
686 686 c.use_jedi = False
687 687 s, matches = c.complete("ip.")
688 688 self.assertNotIn(".__str__", matches)
689 689 self.assertNotIn("._hidden_attr", matches)
690 690
691 691 # c.use_jedi = True
692 692 # completions = set(c.completions('ip.', 3))
693 693 # self.assertNotIn(Completion(3,3,'__str__'), completions)
694 694 # self.assertNotIn(Completion(3,3, "_hidden_attr"), completions)
695 695
696 696 with provisionalcompleter():
697 697 c.use_jedi = False
698 698 s, matches = c.complete("ip._x.")
699 699 self.assertIn(".keys", matches)
700 700
701 701 # c.use_jedi = True
702 702 # completions = set(c.completions('ip._x.', 6))
703 703 # self.assertIn(Completion(6,6, "keys"), completions)
704 704
705 705 del ip._hidden_attr
706 706 del ip._x
707 707
708 708 def test_limit_to__all__False_ok(self):
709 709 """
710 710 Limit to all is deprecated, once we remove it this test can go away.
711 711 """
712 712 ip = get_ipython()
713 713 c = ip.Completer
714 714 c.use_jedi = False
715 715 ip.ex("class D: x=24")
716 716 ip.ex("d=D()")
717 717 cfg = Config()
718 718 cfg.IPCompleter.limit_to__all__ = False
719 719 c.update_config(cfg)
720 720 s, matches = c.complete("d.")
721 721 self.assertIn(".x", matches)
722 722
723 723 def test_get__all__entries_ok(self):
724 724 class A:
725 725 __all__ = ["x", 1]
726 726
727 727 words = completer.get__all__entries(A())
728 728 self.assertEqual(words, ["x"])
729 729
730 730 def test_get__all__entries_no__all__ok(self):
731 731 class A:
732 732 pass
733 733
734 734 words = completer.get__all__entries(A())
735 735 self.assertEqual(words, [])
736 736
737 737 def test_func_kw_completions(self):
738 738 ip = get_ipython()
739 739 c = ip.Completer
740 740 c.use_jedi = False
741 741 ip.ex("def myfunc(a=1,b=2): return a+b")
742 742 s, matches = c.complete(None, "myfunc(1,b")
743 743 self.assertIn("b=", matches)
744 744 # Simulate completing with cursor right after b (pos==10):
745 745 s, matches = c.complete(None, "myfunc(1,b)", 10)
746 746 self.assertIn("b=", matches)
747 747 s, matches = c.complete(None, 'myfunc(a="escaped\\")string",b')
748 748 self.assertIn("b=", matches)
749 749 # builtin function
750 750 s, matches = c.complete(None, "min(k, k")
751 751 self.assertIn("key=", matches)
752 752
753 753 def test_default_arguments_from_docstring(self):
754 754 ip = get_ipython()
755 755 c = ip.Completer
756 756 kwd = c._default_arguments_from_docstring("min(iterable[, key=func]) -> value")
757 757 self.assertEqual(kwd, ["key"])
758 758 # with cython type etc
759 759 kwd = c._default_arguments_from_docstring(
760 760 "Minuit.migrad(self, int ncall=10000, resume=True, int nsplit=1)\n"
761 761 )
762 762 self.assertEqual(kwd, ["ncall", "resume", "nsplit"])
763 763 # white spaces
764 764 kwd = c._default_arguments_from_docstring(
765 765 "\n Minuit.migrad(self, int ncall=10000, resume=True, int nsplit=1)\n"
766 766 )
767 767 self.assertEqual(kwd, ["ncall", "resume", "nsplit"])
768 768
769 769 def test_line_magics(self):
770 770 ip = get_ipython()
771 771 c = ip.Completer
772 772 s, matches = c.complete(None, "lsmag")
773 773 self.assertIn("%lsmagic", matches)
774 774 s, matches = c.complete(None, "%lsmag")
775 775 self.assertIn("%lsmagic", matches)
776 776
777 777 def test_cell_magics(self):
778 778 from IPython.core.magic import register_cell_magic
779 779
780 780 @register_cell_magic
781 781 def _foo_cellm(line, cell):
782 782 pass
783 783
784 784 ip = get_ipython()
785 785 c = ip.Completer
786 786
787 787 s, matches = c.complete(None, "_foo_ce")
788 788 self.assertIn("%%_foo_cellm", matches)
789 789 s, matches = c.complete(None, "%%_foo_ce")
790 790 self.assertIn("%%_foo_cellm", matches)
791 791
792 792 def test_line_cell_magics(self):
793 793 from IPython.core.magic import register_line_cell_magic
794 794
795 795 @register_line_cell_magic
796 796 def _bar_cellm(line, cell):
797 797 pass
798 798
799 799 ip = get_ipython()
800 800 c = ip.Completer
801 801
802 802 # The policy here is trickier, see comments in completion code. The
803 803 # returned values depend on whether the user passes %% or not explicitly,
804 804 # and this will show a difference if the same name is both a line and cell
805 805 # magic.
806 806 s, matches = c.complete(None, "_bar_ce")
807 807 self.assertIn("%_bar_cellm", matches)
808 808 self.assertIn("%%_bar_cellm", matches)
809 809 s, matches = c.complete(None, "%_bar_ce")
810 810 self.assertIn("%_bar_cellm", matches)
811 811 self.assertIn("%%_bar_cellm", matches)
812 812 s, matches = c.complete(None, "%%_bar_ce")
813 813 self.assertNotIn("%_bar_cellm", matches)
814 814 self.assertIn("%%_bar_cellm", matches)
815 815
816 816 def test_magic_completion_order(self):
817 817 ip = get_ipython()
818 818 c = ip.Completer
819 819
820 820 # Test ordering of line and cell magics.
821 821 text, matches = c.complete("timeit")
822 822 self.assertEqual(matches, ["%timeit", "%%timeit"])
823 823
824 824 def test_magic_completion_shadowing(self):
825 825 ip = get_ipython()
826 826 c = ip.Completer
827 827 c.use_jedi = False
828 828
829 829 # Before importing matplotlib, %matplotlib magic should be the only option.
830 830 text, matches = c.complete("mat")
831 831 self.assertEqual(matches, ["%matplotlib"])
832 832
833 833 # The newly introduced name should shadow the magic.
834 834 ip.run_cell("matplotlib = 1")
835 835 text, matches = c.complete("mat")
836 836 self.assertEqual(matches, ["matplotlib"])
837 837
838 838 # After removing matplotlib from namespace, the magic should again be
839 839 # the only option.
840 840 del ip.user_ns["matplotlib"]
841 841 text, matches = c.complete("mat")
842 842 self.assertEqual(matches, ["%matplotlib"])
843 843
844 844 def test_magic_completion_shadowing_explicit(self):
845 845 """
846 846 If the user try to complete a shadowed magic, and explicit % start should
847 847 still return the completions.
848 848 """
849 849 ip = get_ipython()
850 850 c = ip.Completer
851 851
852 852 # Before importing matplotlib, %matplotlib magic should be the only option.
853 853 text, matches = c.complete("%mat")
854 854 self.assertEqual(matches, ["%matplotlib"])
855 855
856 856 ip.run_cell("matplotlib = 1")
857 857
858 858 # After removing matplotlib from namespace, the magic should still be
859 859 # the only option.
860 860 text, matches = c.complete("%mat")
861 861 self.assertEqual(matches, ["%matplotlib"])
862 862
863 863 def test_magic_config(self):
864 864 ip = get_ipython()
865 865 c = ip.Completer
866 866
867 867 s, matches = c.complete(None, "conf")
868 868 self.assertIn("%config", matches)
869 869 s, matches = c.complete(None, "conf")
870 870 self.assertNotIn("AliasManager", matches)
871 871 s, matches = c.complete(None, "config ")
872 872 self.assertIn("AliasManager", matches)
873 873 s, matches = c.complete(None, "%config ")
874 874 self.assertIn("AliasManager", matches)
875 875 s, matches = c.complete(None, "config Ali")
876 876 self.assertListEqual(["AliasManager"], matches)
877 877 s, matches = c.complete(None, "%config Ali")
878 878 self.assertListEqual(["AliasManager"], matches)
879 879 s, matches = c.complete(None, "config AliasManager")
880 880 self.assertListEqual(["AliasManager"], matches)
881 881 s, matches = c.complete(None, "%config AliasManager")
882 882 self.assertListEqual(["AliasManager"], matches)
883 883 s, matches = c.complete(None, "config AliasManager.")
884 884 self.assertIn("AliasManager.default_aliases", matches)
885 885 s, matches = c.complete(None, "%config AliasManager.")
886 886 self.assertIn("AliasManager.default_aliases", matches)
887 887 s, matches = c.complete(None, "config AliasManager.de")
888 888 self.assertListEqual(["AliasManager.default_aliases"], matches)
889 889 s, matches = c.complete(None, "config AliasManager.de")
890 890 self.assertListEqual(["AliasManager.default_aliases"], matches)
891 891
892 892 def test_magic_color(self):
893 893 ip = get_ipython()
894 894 c = ip.Completer
895 895
896 896 s, matches = c.complete(None, "colo")
897 897 self.assertIn("%colors", matches)
898 898 s, matches = c.complete(None, "colo")
899 899 self.assertNotIn("NoColor", matches)
900 900 s, matches = c.complete(None, "%colors") # No trailing space
901 901 self.assertNotIn("NoColor", matches)
902 902 s, matches = c.complete(None, "colors ")
903 903 self.assertIn("NoColor", matches)
904 904 s, matches = c.complete(None, "%colors ")
905 905 self.assertIn("NoColor", matches)
906 906 s, matches = c.complete(None, "colors NoCo")
907 907 self.assertListEqual(["NoColor"], matches)
908 908 s, matches = c.complete(None, "%colors NoCo")
909 909 self.assertListEqual(["NoColor"], matches)
910 910
911 911 def test_match_dict_keys(self):
912 912 """
913 913 Test that match_dict_keys works on a couple of use case does return what
914 914 expected, and does not crash
915 915 """
916 916 delims = " \t\n`!@#$^&*()=+[{]}\\|;:'\",<>?"
917 917
918 918 def match(*args, **kwargs):
919 919 quote, offset, matches = match_dict_keys(*args, delims=delims, **kwargs)
920 920 return quote, offset, list(matches)
921 921
922 922 keys = ["foo", b"far"]
923 923 assert match(keys, "b'") == ("'", 2, ["far"])
924 924 assert match(keys, "b'f") == ("'", 2, ["far"])
925 925 assert match(keys, 'b"') == ('"', 2, ["far"])
926 926 assert match(keys, 'b"f') == ('"', 2, ["far"])
927 927
928 928 assert match(keys, "'") == ("'", 1, ["foo"])
929 929 assert match(keys, "'f") == ("'", 1, ["foo"])
930 930 assert match(keys, '"') == ('"', 1, ["foo"])
931 931 assert match(keys, '"f') == ('"', 1, ["foo"])
932 932
933 933 # Completion on first item of tuple
934 934 keys = [("foo", 1111), ("foo", 2222), (3333, "bar"), (3333, "test")]
935 935 assert match(keys, "'f") == ("'", 1, ["foo"])
936 936 assert match(keys, "33") == ("", 0, ["3333"])
937 937
938 938 # Completion on numbers
939 939 keys = [
940 940 0xDEADBEEF,
941 941 1111,
942 942 1234,
943 943 "1999",
944 944 0b10101,
945 945 22,
946 946 ] # 0xDEADBEEF = 3735928559; 0b10101 = 21
947 947 assert match(keys, "0xdead") == ("", 0, ["0xdeadbeef"])
948 948 assert match(keys, "1") == ("", 0, ["1111", "1234"])
949 949 assert match(keys, "2") == ("", 0, ["21", "22"])
950 950 assert match(keys, "0b101") == ("", 0, ["0b10101", "0b10110"])
951 951
952 952 # Should yield on variables
953 953 assert match(keys, "a_variable") == ("", 0, [])
954 954
955 955 # Should pass over invalid literals
956 956 assert match(keys, "'' ''") == ("", 0, [])
957 957
958 958 def test_match_dict_keys_tuple(self):
959 959 """
960 960 Test that match_dict_keys called with extra prefix works on a couple of use case,
961 961 does return what expected, and does not crash.
962 962 """
963 963 delims = " \t\n`!@#$^&*()=+[{]}\\|;:'\",<>?"
964 964
965 965 keys = [("foo", "bar"), ("foo", "oof"), ("foo", b"bar"), ('other', 'test')]
966 966
967 967 def match(*args, extra=None, **kwargs):
968 968 quote, offset, matches = match_dict_keys(
969 969 *args, delims=delims, extra_prefix=extra, **kwargs
970 970 )
971 971 return quote, offset, list(matches)
972 972
973 973 # Completion on first key == "foo"
974 974 assert match(keys, "'", extra=("foo",)) == ("'", 1, ["bar", "oof"])
975 975 assert match(keys, '"', extra=("foo",)) == ('"', 1, ["bar", "oof"])
976 976 assert match(keys, "'o", extra=("foo",)) == ("'", 1, ["oof"])
977 977 assert match(keys, '"o', extra=("foo",)) == ('"', 1, ["oof"])
978 978 assert match(keys, "b'", extra=("foo",)) == ("'", 2, ["bar"])
979 979 assert match(keys, 'b"', extra=("foo",)) == ('"', 2, ["bar"])
980 980 assert match(keys, "b'b", extra=("foo",)) == ("'", 2, ["bar"])
981 981 assert match(keys, 'b"b', extra=("foo",)) == ('"', 2, ["bar"])
982 982
983 983 # No Completion
984 984 assert match(keys, "'", extra=("no_foo",)) == ("'", 1, [])
985 985 assert match(keys, "'", extra=("fo",)) == ("'", 1, [])
986 986
987 987 keys = [("foo1", "foo2", "foo3", "foo4"), ("foo1", "foo2", "bar", "foo4")]
988 988 assert match(keys, "'foo", extra=("foo1",)) == ("'", 1, ["foo2"])
989 989 assert match(keys, "'foo", extra=("foo1", "foo2")) == ("'", 1, ["foo3"])
990 990 assert match(keys, "'foo", extra=("foo1", "foo2", "foo3")) == ("'", 1, ["foo4"])
991 991 assert match(keys, "'foo", extra=("foo1", "foo2", "foo3", "foo4")) == (
992 992 "'",
993 993 1,
994 994 [],
995 995 )
996 996
997 997 keys = [("foo", 1111), ("foo", "2222"), (3333, "bar"), (3333, 4444)]
998 998 assert match(keys, "'", extra=("foo",)) == ("'", 1, ["2222"])
999 999 assert match(keys, "", extra=("foo",)) == ("", 0, ["1111", "'2222'"])
1000 1000 assert match(keys, "'", extra=(3333,)) == ("'", 1, ["bar"])
1001 1001 assert match(keys, "", extra=(3333,)) == ("", 0, ["'bar'", "4444"])
1002 1002 assert match(keys, "'", extra=("3333",)) == ("'", 1, [])
1003 1003 assert match(keys, "33") == ("", 0, ["3333"])
1004 1004
1005 1005 def test_dict_key_completion_closures(self):
1006 1006 ip = get_ipython()
1007 1007 complete = ip.Completer.complete
1008 1008 ip.Completer.auto_close_dict_keys = True
1009 1009
1010 1010 ip.user_ns["d"] = {
1011 1011 # tuple only
1012 1012 ("aa", 11): None,
1013 1013 # tuple and non-tuple
1014 1014 ("bb", 22): None,
1015 1015 "bb": None,
1016 1016 # non-tuple only
1017 1017 "cc": None,
1018 1018 # numeric tuple only
1019 1019 (77, "x"): None,
1020 1020 # numeric tuple and non-tuple
1021 1021 (88, "y"): None,
1022 1022 88: None,
1023 1023 # numeric non-tuple only
1024 1024 99: None,
1025 1025 }
1026 1026
1027 1027 _, matches = complete(line_buffer="d[")
1028 1028 # should append `, ` if matches a tuple only
1029 1029 self.assertIn("'aa', ", matches)
1030 1030 # should not append anything if matches a tuple and an item
1031 1031 self.assertIn("'bb'", matches)
1032 1032 # should append `]` if matches and item only
1033 1033 self.assertIn("'cc']", matches)
1034 1034
1035 1035 # should append `, ` if matches a tuple only
1036 1036 self.assertIn("77, ", matches)
1037 1037 # should not append anything if matches a tuple and an item
1038 1038 self.assertIn("88", matches)
1039 1039 # should append `]` if matches and item only
1040 1040 self.assertIn("99]", matches)
1041 1041
1042 1042 _, matches = complete(line_buffer="d['aa', ")
1043 1043 # should restrict matches to those matching tuple prefix
1044 1044 self.assertIn("11]", matches)
1045 1045 self.assertNotIn("'bb'", matches)
1046 1046 self.assertNotIn("'bb', ", matches)
1047 1047 self.assertNotIn("'bb']", matches)
1048 1048 self.assertNotIn("'cc'", matches)
1049 1049 self.assertNotIn("'cc', ", matches)
1050 1050 self.assertNotIn("'cc']", matches)
1051 1051 ip.Completer.auto_close_dict_keys = False
1052 1052
1053 1053 def test_dict_key_completion_string(self):
1054 1054 """Test dictionary key completion for string keys"""
1055 1055 ip = get_ipython()
1056 1056 complete = ip.Completer.complete
1057 1057
1058 1058 ip.user_ns["d"] = {"abc": None}
1059 1059
1060 1060 # check completion at different stages
1061 1061 _, matches = complete(line_buffer="d[")
1062 1062 self.assertIn("'abc'", matches)
1063 1063 self.assertNotIn("'abc']", matches)
1064 1064
1065 1065 _, matches = complete(line_buffer="d['")
1066 1066 self.assertIn("abc", matches)
1067 1067 self.assertNotIn("abc']", matches)
1068 1068
1069 1069 _, matches = complete(line_buffer="d['a")
1070 1070 self.assertIn("abc", matches)
1071 1071 self.assertNotIn("abc']", matches)
1072 1072
1073 1073 # check use of different quoting
1074 1074 _, matches = complete(line_buffer='d["')
1075 1075 self.assertIn("abc", matches)
1076 1076 self.assertNotIn('abc"]', matches)
1077 1077
1078 1078 _, matches = complete(line_buffer='d["a')
1079 1079 self.assertIn("abc", matches)
1080 1080 self.assertNotIn('abc"]', matches)
1081 1081
1082 1082 # check sensitivity to following context
1083 1083 _, matches = complete(line_buffer="d[]", cursor_pos=2)
1084 1084 self.assertIn("'abc'", matches)
1085 1085
1086 1086 _, matches = complete(line_buffer="d['']", cursor_pos=3)
1087 1087 self.assertIn("abc", matches)
1088 1088 self.assertNotIn("abc'", matches)
1089 1089 self.assertNotIn("abc']", matches)
1090 1090
1091 1091 # check multiple solutions are correctly returned and that noise is not
1092 1092 ip.user_ns["d"] = {
1093 1093 "abc": None,
1094 1094 "abd": None,
1095 1095 "bad": None,
1096 1096 object(): None,
1097 1097 5: None,
1098 1098 ("abe", None): None,
1099 1099 (None, "abf"): None
1100 1100 }
1101 1101
1102 1102 _, matches = complete(line_buffer="d['a")
1103 1103 self.assertIn("abc", matches)
1104 1104 self.assertIn("abd", matches)
1105 1105 self.assertNotIn("bad", matches)
1106 1106 self.assertNotIn("abe", matches)
1107 1107 self.assertNotIn("abf", matches)
1108 1108 assert not any(m.endswith(("]", '"', "'")) for m in matches), matches
1109 1109
1110 1110 # check escaping and whitespace
1111 1111 ip.user_ns["d"] = {"a\nb": None, "a'b": None, 'a"b': None, "a word": None}
1112 1112 _, matches = complete(line_buffer="d['a")
1113 1113 self.assertIn("a\\nb", matches)
1114 1114 self.assertIn("a\\'b", matches)
1115 1115 self.assertIn('a"b', matches)
1116 1116 self.assertIn("a word", matches)
1117 1117 assert not any(m.endswith(("]", '"', "'")) for m in matches), matches
1118 1118
1119 1119 # - can complete on non-initial word of the string
1120 1120 _, matches = complete(line_buffer="d['a w")
1121 1121 self.assertIn("word", matches)
1122 1122
1123 1123 # - understands quote escaping
1124 1124 _, matches = complete(line_buffer="d['a\\'")
1125 1125 self.assertIn("b", matches)
1126 1126
1127 1127 # - default quoting should work like repr
1128 1128 _, matches = complete(line_buffer="d[")
1129 1129 self.assertIn('"a\'b"', matches)
1130 1130
1131 1131 # - when opening quote with ", possible to match with unescaped apostrophe
1132 1132 _, matches = complete(line_buffer="d[\"a'")
1133 1133 self.assertIn("b", matches)
1134 1134
1135 1135 # need to not split at delims that readline won't split at
1136 1136 if "-" not in ip.Completer.splitter.delims:
1137 1137 ip.user_ns["d"] = {"before-after": None}
1138 1138 _, matches = complete(line_buffer="d['before-af")
1139 1139 self.assertIn("before-after", matches)
1140 1140
1141 1141 # check completion on tuple-of-string keys at different stage - on first key
1142 1142 ip.user_ns["d"] = {('foo', 'bar'): None}
1143 1143 _, matches = complete(line_buffer="d[")
1144 1144 self.assertIn("'foo'", matches)
1145 1145 self.assertNotIn("'foo']", matches)
1146 1146 self.assertNotIn("'bar'", matches)
1147 1147 self.assertNotIn("foo", matches)
1148 1148 self.assertNotIn("bar", matches)
1149 1149
1150 1150 # - match the prefix
1151 1151 _, matches = complete(line_buffer="d['f")
1152 1152 self.assertIn("foo", matches)
1153 1153 self.assertNotIn("foo']", matches)
1154 1154 self.assertNotIn('foo"]', matches)
1155 1155 _, matches = complete(line_buffer="d['foo")
1156 1156 self.assertIn("foo", matches)
1157 1157
1158 1158 # - can complete on second key
1159 1159 _, matches = complete(line_buffer="d['foo', ")
1160 1160 self.assertIn("'bar'", matches)
1161 1161 _, matches = complete(line_buffer="d['foo', 'b")
1162 1162 self.assertIn("bar", matches)
1163 1163 self.assertNotIn("foo", matches)
1164 1164
1165 1165 # - does not propose missing keys
1166 1166 _, matches = complete(line_buffer="d['foo', 'f")
1167 1167 self.assertNotIn("bar", matches)
1168 1168 self.assertNotIn("foo", matches)
1169 1169
1170 1170 # check sensitivity to following context
1171 1171 _, matches = complete(line_buffer="d['foo',]", cursor_pos=8)
1172 1172 self.assertIn("'bar'", matches)
1173 1173 self.assertNotIn("bar", matches)
1174 1174 self.assertNotIn("'foo'", matches)
1175 1175 self.assertNotIn("foo", matches)
1176 1176
1177 1177 _, matches = complete(line_buffer="d['']", cursor_pos=3)
1178 1178 self.assertIn("foo", matches)
1179 1179 assert not any(m.endswith(("]", '"', "'")) for m in matches), matches
1180 1180
1181 1181 _, matches = complete(line_buffer='d[""]', cursor_pos=3)
1182 1182 self.assertIn("foo", matches)
1183 1183 assert not any(m.endswith(("]", '"', "'")) for m in matches), matches
1184 1184
1185 1185 _, matches = complete(line_buffer='d["foo","]', cursor_pos=9)
1186 1186 self.assertIn("bar", matches)
1187 1187 assert not any(m.endswith(("]", '"', "'")) for m in matches), matches
1188 1188
1189 1189 _, matches = complete(line_buffer='d["foo",]', cursor_pos=8)
1190 1190 self.assertIn("'bar'", matches)
1191 1191 self.assertNotIn("bar", matches)
1192 1192
1193 1193 # Can complete with longer tuple keys
1194 1194 ip.user_ns["d"] = {('foo', 'bar', 'foobar'): None}
1195 1195
1196 1196 # - can complete second key
1197 1197 _, matches = complete(line_buffer="d['foo', 'b")
1198 1198 self.assertIn("bar", matches)
1199 1199 self.assertNotIn("foo", matches)
1200 1200 self.assertNotIn("foobar", matches)
1201 1201
1202 1202 # - can complete third key
1203 1203 _, matches = complete(line_buffer="d['foo', 'bar', 'fo")
1204 1204 self.assertIn("foobar", matches)
1205 1205 self.assertNotIn("foo", matches)
1206 1206 self.assertNotIn("bar", matches)
1207 1207
1208 1208 def test_dict_key_completion_numbers(self):
1209 1209 ip = get_ipython()
1210 1210 complete = ip.Completer.complete
1211 1211
1212 1212 ip.user_ns["d"] = {
1213 1213 0xDEADBEEF: None, # 3735928559
1214 1214 1111: None,
1215 1215 1234: None,
1216 1216 "1999": None,
1217 1217 0b10101: None, # 21
1218 1218 22: None,
1219 1219 }
1220 1220 _, matches = complete(line_buffer="d[1")
1221 1221 self.assertIn("1111", matches)
1222 1222 self.assertIn("1234", matches)
1223 1223 self.assertNotIn("1999", matches)
1224 1224 self.assertNotIn("'1999'", matches)
1225 1225
1226 1226 _, matches = complete(line_buffer="d[0xdead")
1227 1227 self.assertIn("0xdeadbeef", matches)
1228 1228
1229 1229 _, matches = complete(line_buffer="d[2")
1230 1230 self.assertIn("21", matches)
1231 1231 self.assertIn("22", matches)
1232 1232
1233 1233 _, matches = complete(line_buffer="d[0b101")
1234 1234 self.assertIn("0b10101", matches)
1235 1235 self.assertIn("0b10110", matches)
1236 1236
1237 1237 def test_dict_key_completion_contexts(self):
1238 1238 """Test expression contexts in which dict key completion occurs"""
1239 1239 ip = get_ipython()
1240 1240 complete = ip.Completer.complete
1241 1241 d = {"abc": None}
1242 1242 ip.user_ns["d"] = d
1243 1243
1244 1244 class C:
1245 1245 data = d
1246 1246
1247 1247 ip.user_ns["C"] = C
1248 1248 ip.user_ns["get"] = lambda: d
1249 1249 ip.user_ns["nested"] = {"x": d}
1250 1250
1251 1251 def assert_no_completion(**kwargs):
1252 1252 _, matches = complete(**kwargs)
1253 1253 self.assertNotIn("abc", matches)
1254 1254 self.assertNotIn("abc'", matches)
1255 1255 self.assertNotIn("abc']", matches)
1256 1256 self.assertNotIn("'abc'", matches)
1257 1257 self.assertNotIn("'abc']", matches)
1258 1258
1259 1259 def assert_completion(**kwargs):
1260 1260 _, matches = complete(**kwargs)
1261 1261 self.assertIn("'abc'", matches)
1262 1262 self.assertNotIn("'abc']", matches)
1263 1263
1264 1264 # no completion after string closed, even if reopened
1265 1265 assert_no_completion(line_buffer="d['a'")
1266 1266 assert_no_completion(line_buffer='d["a"')
1267 1267 assert_no_completion(line_buffer="d['a' + ")
1268 1268 assert_no_completion(line_buffer="d['a' + '")
1269 1269
1270 1270 # completion in non-trivial expressions
1271 1271 assert_completion(line_buffer="+ d[")
1272 1272 assert_completion(line_buffer="(d[")
1273 1273 assert_completion(line_buffer="C.data[")
1274 1274
1275 1275 # nested dict completion
1276 1276 assert_completion(line_buffer="nested['x'][")
1277 1277
1278 1278 with evaluation_policy("minimal"):
1279 1279 with pytest.raises(AssertionError):
1280 1280 assert_completion(line_buffer="nested['x'][")
1281 1281
1282 1282 # greedy flag
1283 1283 def assert_completion(**kwargs):
1284 1284 _, matches = complete(**kwargs)
1285 1285 self.assertIn("get()['abc']", matches)
1286 1286
1287 1287 assert_no_completion(line_buffer="get()[")
1288 1288 with greedy_completion():
1289 1289 assert_completion(line_buffer="get()[")
1290 1290 assert_completion(line_buffer="get()['")
1291 1291 assert_completion(line_buffer="get()['a")
1292 1292 assert_completion(line_buffer="get()['ab")
1293 1293 assert_completion(line_buffer="get()['abc")
1294 1294
1295 1295 def test_dict_key_completion_bytes(self):
1296 1296 """Test handling of bytes in dict key completion"""
1297 1297 ip = get_ipython()
1298 1298 complete = ip.Completer.complete
1299 1299
1300 1300 ip.user_ns["d"] = {"abc": None, b"abd": None}
1301 1301
1302 1302 _, matches = complete(line_buffer="d[")
1303 1303 self.assertIn("'abc'", matches)
1304 1304 self.assertIn("b'abd'", matches)
1305 1305
1306 1306 if False: # not currently implemented
1307 1307 _, matches = complete(line_buffer="d[b")
1308 1308 self.assertIn("b'abd'", matches)
1309 1309 self.assertNotIn("b'abc'", matches)
1310 1310
1311 1311 _, matches = complete(line_buffer="d[b'")
1312 1312 self.assertIn("abd", matches)
1313 1313 self.assertNotIn("abc", matches)
1314 1314
1315 1315 _, matches = complete(line_buffer="d[B'")
1316 1316 self.assertIn("abd", matches)
1317 1317 self.assertNotIn("abc", matches)
1318 1318
1319 1319 _, matches = complete(line_buffer="d['")
1320 1320 self.assertIn("abc", matches)
1321 1321 self.assertNotIn("abd", matches)
1322 1322
1323 1323 def test_dict_key_completion_unicode_py3(self):
1324 1324 """Test handling of unicode in dict key completion"""
1325 1325 ip = get_ipython()
1326 1326 complete = ip.Completer.complete
1327 1327
1328 1328 ip.user_ns["d"] = {"a\u05d0": None}
1329 1329
1330 1330 # query using escape
1331 1331 if sys.platform != "win32":
1332 1332 # Known failure on Windows
1333 1333 _, matches = complete(line_buffer="d['a\\u05d0")
1334 1334 self.assertIn("u05d0", matches) # tokenized after \\
1335 1335
1336 1336 # query using character
1337 1337 _, matches = complete(line_buffer="d['a\u05d0")
1338 1338 self.assertIn("a\u05d0", matches)
1339 1339
1340 1340 with greedy_completion():
1341 1341 # query using escape
1342 1342 _, matches = complete(line_buffer="d['a\\u05d0")
1343 1343 self.assertIn("d['a\\u05d0']", matches) # tokenized after \\
1344 1344
1345 1345 # query using character
1346 1346 _, matches = complete(line_buffer="d['a\u05d0")
1347 1347 self.assertIn("d['a\u05d0']", matches)
1348 1348
1349 1349 @dec.skip_without("numpy")
1350 1350 def test_struct_array_key_completion(self):
1351 1351 """Test dict key completion applies to numpy struct arrays"""
1352 1352 import numpy
1353 1353
1354 1354 ip = get_ipython()
1355 1355 complete = ip.Completer.complete
1356 1356 ip.user_ns["d"] = numpy.array([], dtype=[("hello", "f"), ("world", "f")])
1357 1357 _, matches = complete(line_buffer="d['")
1358 1358 self.assertIn("hello", matches)
1359 1359 self.assertIn("world", matches)
1360 1360 # complete on the numpy struct itself
1361 1361 dt = numpy.dtype(
1362 1362 [("my_head", [("my_dt", ">u4"), ("my_df", ">u4")]), ("my_data", ">f4", 5)]
1363 1363 )
1364 1364 x = numpy.zeros(2, dtype=dt)
1365 1365 ip.user_ns["d"] = x[1]
1366 1366 _, matches = complete(line_buffer="d['")
1367 1367 self.assertIn("my_head", matches)
1368 1368 self.assertIn("my_data", matches)
1369 1369
1370 1370 def completes_on_nested():
1371 1371 ip.user_ns["d"] = numpy.zeros(2, dtype=dt)
1372 1372 _, matches = complete(line_buffer="d[1]['my_head']['")
1373 1373 self.assertTrue(any(["my_dt" in m for m in matches]))
1374 1374 self.assertTrue(any(["my_df" in m for m in matches]))
1375 1375 # complete on a nested level
1376 1376 with greedy_completion():
1377 1377 completes_on_nested()
1378 1378
1379 1379 with evaluation_policy("limited"):
1380 1380 completes_on_nested()
1381 1381
1382 1382 with evaluation_policy("minimal"):
1383 1383 with pytest.raises(AssertionError):
1384 1384 completes_on_nested()
1385 1385
1386 1386 @dec.skip_without("pandas")
1387 1387 def test_dataframe_key_completion(self):
1388 1388 """Test dict key completion applies to pandas DataFrames"""
1389 1389 import pandas
1390 1390
1391 1391 ip = get_ipython()
1392 1392 complete = ip.Completer.complete
1393 1393 ip.user_ns["d"] = pandas.DataFrame({"hello": [1], "world": [2]})
1394 1394 _, matches = complete(line_buffer="d['")
1395 1395 self.assertIn("hello", matches)
1396 1396 self.assertIn("world", matches)
1397 1397 _, matches = complete(line_buffer="d.loc[:, '")
1398 1398 self.assertIn("hello", matches)
1399 1399 self.assertIn("world", matches)
1400 1400 _, matches = complete(line_buffer="d.loc[1:, '")
1401 1401 self.assertIn("hello", matches)
1402 1402 _, matches = complete(line_buffer="d.loc[1:1, '")
1403 1403 self.assertIn("hello", matches)
1404 1404 _, matches = complete(line_buffer="d.loc[1:1:-1, '")
1405 1405 self.assertIn("hello", matches)
1406 1406 _, matches = complete(line_buffer="d.loc[::, '")
1407 1407 self.assertIn("hello", matches)
1408 1408
1409 1409 def test_dict_key_completion_invalids(self):
1410 1410 """Smoke test cases dict key completion can't handle"""
1411 1411 ip = get_ipython()
1412 1412 complete = ip.Completer.complete
1413 1413
1414 1414 ip.user_ns["no_getitem"] = None
1415 1415 ip.user_ns["no_keys"] = []
1416 1416 ip.user_ns["cant_call_keys"] = dict
1417 1417 ip.user_ns["empty"] = {}
1418 1418 ip.user_ns["d"] = {"abc": 5}
1419 1419
1420 1420 _, matches = complete(line_buffer="no_getitem['")
1421 1421 _, matches = complete(line_buffer="no_keys['")
1422 1422 _, matches = complete(line_buffer="cant_call_keys['")
1423 1423 _, matches = complete(line_buffer="empty['")
1424 1424 _, matches = complete(line_buffer="name_error['")
1425 1425 _, matches = complete(line_buffer="d['\\") # incomplete escape
1426 1426
1427 1427 def test_object_key_completion(self):
1428 1428 ip = get_ipython()
1429 1429 ip.user_ns["key_completable"] = KeyCompletable(["qwerty", "qwick"])
1430 1430
1431 1431 _, matches = ip.Completer.complete(line_buffer="key_completable['qw")
1432 1432 self.assertIn("qwerty", matches)
1433 1433 self.assertIn("qwick", matches)
1434 1434
1435 1435 def test_class_key_completion(self):
1436 1436 ip = get_ipython()
1437 1437 NamedInstanceClass("qwerty")
1438 1438 NamedInstanceClass("qwick")
1439 1439 ip.user_ns["named_instance_class"] = NamedInstanceClass
1440 1440
1441 1441 _, matches = ip.Completer.complete(line_buffer="named_instance_class['qw")
1442 1442 self.assertIn("qwerty", matches)
1443 1443 self.assertIn("qwick", matches)
1444 1444
1445 1445 def test_tryimport(self):
1446 1446 """
1447 1447 Test that try-import don't crash on trailing dot, and import modules before
1448 1448 """
1449 1449 from IPython.core.completerlib import try_import
1450 1450
1451 1451 assert try_import("IPython.")
1452 1452
1453 1453 def test_aimport_module_completer(self):
1454 1454 ip = get_ipython()
1455 1455 _, matches = ip.complete("i", "%aimport i")
1456 1456 self.assertIn("io", matches)
1457 1457 self.assertNotIn("int", matches)
1458 1458
1459 1459 def test_nested_import_module_completer(self):
1460 1460 ip = get_ipython()
1461 1461 _, matches = ip.complete(None, "import IPython.co", 17)
1462 1462 self.assertIn("IPython.core", matches)
1463 1463 self.assertNotIn("import IPython.core", matches)
1464 1464 self.assertNotIn("IPython.display", matches)
1465 1465
1466 1466 def test_import_module_completer(self):
1467 1467 ip = get_ipython()
1468 1468 _, matches = ip.complete("i", "import i")
1469 1469 self.assertIn("io", matches)
1470 1470 self.assertNotIn("int", matches)
1471 1471
1472 1472 def test_from_module_completer(self):
1473 1473 ip = get_ipython()
1474 1474 _, matches = ip.complete("B", "from io import B", 16)
1475 1475 self.assertIn("BytesIO", matches)
1476 1476 self.assertNotIn("BaseException", matches)
1477 1477
1478 1478 def test_snake_case_completion(self):
1479 1479 ip = get_ipython()
1480 1480 ip.Completer.use_jedi = False
1481 1481 ip.user_ns["some_three"] = 3
1482 1482 ip.user_ns["some_four"] = 4
1483 1483 _, matches = ip.complete("s_", "print(s_f")
1484 1484 self.assertIn("some_three", matches)
1485 1485 self.assertIn("some_four", matches)
1486 1486
1487 1487 def test_mix_terms(self):
1488 1488 ip = get_ipython()
1489 1489 from textwrap import dedent
1490 1490
1491 1491 ip.Completer.use_jedi = False
1492 1492 ip.ex(
1493 1493 dedent(
1494 1494 """
1495 1495 class Test:
1496 1496 def meth(self, meth_arg1):
1497 1497 print("meth")
1498 1498
1499 1499 def meth_1(self, meth1_arg1, meth1_arg2):
1500 1500 print("meth1")
1501 1501
1502 1502 def meth_2(self, meth2_arg1, meth2_arg2):
1503 1503 print("meth2")
1504 1504 test = Test()
1505 1505 """
1506 1506 )
1507 1507 )
1508 1508 _, matches = ip.complete(None, "test.meth(")
1509 1509 self.assertIn("meth_arg1=", matches)
1510 1510 self.assertNotIn("meth2_arg1=", matches)
1511 1511
1512 1512 def test_percent_symbol_restrict_to_magic_completions(self):
1513 1513 ip = get_ipython()
1514 1514 completer = ip.Completer
1515 1515 text = "%a"
1516 1516
1517 1517 with provisionalcompleter():
1518 1518 completer.use_jedi = True
1519 1519 completions = completer.completions(text, len(text))
1520 1520 for c in completions:
1521 1521 self.assertEqual(c.text[0], "%")
1522 1522
1523 1523 def test_fwd_unicode_restricts(self):
1524 1524 ip = get_ipython()
1525 1525 completer = ip.Completer
1526 1526 text = "\\ROMAN NUMERAL FIVE"
1527 1527
1528 1528 with provisionalcompleter():
1529 1529 completer.use_jedi = True
1530 1530 completions = [
1531 1531 completion.text for completion in completer.completions(text, len(text))
1532 1532 ]
1533 1533 self.assertEqual(completions, ["\u2164"])
1534 1534
1535 1535 def test_dict_key_restrict_to_dicts(self):
1536 1536 """Test that dict key suppresses non-dict completion items"""
1537 1537 ip = get_ipython()
1538 1538 c = ip.Completer
1539 1539 d = {"abc": None}
1540 1540 ip.user_ns["d"] = d
1541 1541
1542 1542 text = 'd["a'
1543 1543
1544 1544 def _():
1545 1545 with provisionalcompleter():
1546 1546 c.use_jedi = True
1547 1547 return [
1548 1548 completion.text for completion in c.completions(text, len(text))
1549 1549 ]
1550 1550
1551 1551 completions = _()
1552 1552 self.assertEqual(completions, ["abc"])
1553 1553
1554 1554 # check that it can be disabled in granular manner:
1555 1555 cfg = Config()
1556 1556 cfg.IPCompleter.suppress_competing_matchers = {
1557 1557 "IPCompleter.dict_key_matcher": False
1558 1558 }
1559 1559 c.update_config(cfg)
1560 1560
1561 1561 completions = _()
1562 1562 self.assertIn("abc", completions)
1563 1563 self.assertGreater(len(completions), 1)
1564 1564
1565 1565 def test_matcher_suppression(self):
1566 1566 @completion_matcher(identifier="a_matcher")
1567 1567 def a_matcher(text):
1568 1568 return ["completion_a"]
1569 1569
1570 1570 @completion_matcher(identifier="b_matcher", api_version=2)
1571 1571 def b_matcher(context: CompletionContext):
1572 1572 text = context.token
1573 1573 result = {"completions": [SimpleCompletion("completion_b")]}
1574 1574
1575 1575 if text == "suppress c":
1576 1576 result["suppress"] = {"c_matcher"}
1577 1577
1578 1578 if text.startswith("suppress all"):
1579 1579 result["suppress"] = True
1580 1580 if text == "suppress all but c":
1581 1581 result["do_not_suppress"] = {"c_matcher"}
1582 1582 if text == "suppress all but a":
1583 1583 result["do_not_suppress"] = {"a_matcher"}
1584 1584
1585 1585 return result
1586 1586
1587 1587 @completion_matcher(identifier="c_matcher")
1588 1588 def c_matcher(text):
1589 1589 return ["completion_c"]
1590 1590
1591 1591 with custom_matchers([a_matcher, b_matcher, c_matcher]):
1592 1592 ip = get_ipython()
1593 1593 c = ip.Completer
1594 1594
1595 1595 def _(text, expected):
1596 1596 c.use_jedi = False
1597 1597 s, matches = c.complete(text)
1598 1598 self.assertEqual(expected, matches)
1599 1599
1600 1600 _("do not suppress", ["completion_a", "completion_b", "completion_c"])
1601 1601 _("suppress all", ["completion_b"])
1602 1602 _("suppress all but a", ["completion_a", "completion_b"])
1603 1603 _("suppress all but c", ["completion_b", "completion_c"])
1604 1604
1605 1605 def configure(suppression_config):
1606 1606 cfg = Config()
1607 1607 cfg.IPCompleter.suppress_competing_matchers = suppression_config
1608 1608 c.update_config(cfg)
1609 1609
1610 1610 # test that configuration takes priority over the run-time decisions
1611 1611
1612 1612 configure(False)
1613 1613 _("suppress all", ["completion_a", "completion_b", "completion_c"])
1614 1614
1615 1615 configure({"b_matcher": False})
1616 1616 _("suppress all", ["completion_a", "completion_b", "completion_c"])
1617 1617
1618 1618 configure({"a_matcher": False})
1619 1619 _("suppress all", ["completion_b"])
1620 1620
1621 1621 configure({"b_matcher": True})
1622 1622 _("do not suppress", ["completion_b"])
1623 1623
1624 1624 configure(True)
1625 1625 _("do not suppress", ["completion_a"])
1626 1626
1627 1627 def test_matcher_suppression_with_iterator(self):
1628 1628 @completion_matcher(identifier="matcher_returning_iterator")
1629 1629 def matcher_returning_iterator(text):
1630 1630 return iter(["completion_iter"])
1631 1631
1632 1632 @completion_matcher(identifier="matcher_returning_list")
1633 1633 def matcher_returning_list(text):
1634 1634 return ["completion_list"]
1635 1635
1636 1636 with custom_matchers([matcher_returning_iterator, matcher_returning_list]):
1637 1637 ip = get_ipython()
1638 1638 c = ip.Completer
1639 1639
1640 1640 def _(text, expected):
1641 1641 c.use_jedi = False
1642 1642 s, matches = c.complete(text)
1643 1643 self.assertEqual(expected, matches)
1644 1644
1645 1645 def configure(suppression_config):
1646 1646 cfg = Config()
1647 1647 cfg.IPCompleter.suppress_competing_matchers = suppression_config
1648 1648 c.update_config(cfg)
1649 1649
1650 1650 configure(False)
1651 1651 _("---", ["completion_iter", "completion_list"])
1652 1652
1653 1653 configure(True)
1654 1654 _("---", ["completion_iter"])
1655 1655
1656 1656 configure(None)
1657 1657 _("--", ["completion_iter", "completion_list"])
1658 1658
1659 1659 @pytest.mark.xfail(
1660 1660 sys.version_info.releaselevel in ("alpha",),
1661 1661 reason="Parso does not yet parse 3.13",
1662 1662 )
1663 1663 def test_matcher_suppression_with_jedi(self):
1664 1664 ip = get_ipython()
1665 1665 c = ip.Completer
1666 1666 c.use_jedi = True
1667 1667
1668 1668 def configure(suppression_config):
1669 1669 cfg = Config()
1670 1670 cfg.IPCompleter.suppress_competing_matchers = suppression_config
1671 1671 c.update_config(cfg)
1672 1672
1673 1673 def _():
1674 1674 with provisionalcompleter():
1675 1675 matches = [completion.text for completion in c.completions("dict.", 5)]
1676 1676 self.assertIn("keys", matches)
1677 1677
1678 1678 configure(False)
1679 1679 _()
1680 1680
1681 1681 configure(True)
1682 1682 _()
1683 1683
1684 1684 configure(None)
1685 1685 _()
1686 1686
1687 1687 def test_matcher_disabling(self):
1688 1688 @completion_matcher(identifier="a_matcher")
1689 1689 def a_matcher(text):
1690 1690 return ["completion_a"]
1691 1691
1692 1692 @completion_matcher(identifier="b_matcher")
1693 1693 def b_matcher(text):
1694 1694 return ["completion_b"]
1695 1695
1696 1696 def _(expected):
1697 1697 s, matches = c.complete("completion_")
1698 1698 self.assertEqual(expected, matches)
1699 1699
1700 1700 with custom_matchers([a_matcher, b_matcher]):
1701 1701 ip = get_ipython()
1702 1702 c = ip.Completer
1703 1703
1704 1704 _(["completion_a", "completion_b"])
1705 1705
1706 1706 cfg = Config()
1707 1707 cfg.IPCompleter.disable_matchers = ["b_matcher"]
1708 1708 c.update_config(cfg)
1709 1709
1710 1710 _(["completion_a"])
1711 1711
1712 1712 cfg.IPCompleter.disable_matchers = []
1713 1713 c.update_config(cfg)
1714 1714
1715 1715 def test_matcher_priority(self):
1716 1716 @completion_matcher(identifier="a_matcher", priority=0, api_version=2)
1717 1717 def a_matcher(text):
1718 1718 return {"completions": [SimpleCompletion("completion_a")], "suppress": True}
1719 1719
1720 1720 @completion_matcher(identifier="b_matcher", priority=2, api_version=2)
1721 1721 def b_matcher(text):
1722 1722 return {"completions": [SimpleCompletion("completion_b")], "suppress": True}
1723 1723
1724 1724 def _(expected):
1725 1725 s, matches = c.complete("completion_")
1726 1726 self.assertEqual(expected, matches)
1727 1727
1728 1728 with custom_matchers([a_matcher, b_matcher]):
1729 1729 ip = get_ipython()
1730 1730 c = ip.Completer
1731 1731
1732 1732 _(["completion_b"])
1733 1733 a_matcher.matcher_priority = 3
1734 1734 _(["completion_a"])
1735 1735
1736 1736
1737 1737 @pytest.mark.parametrize(
1738 1738 "input, expected",
1739 1739 [
1740 1740 ["1.234", "1.234"],
1741 1741 # should match signed numbers
1742 1742 ["+1", "+1"],
1743 1743 ["-1", "-1"],
1744 1744 ["-1.0", "-1.0"],
1745 1745 ["-1.", "-1."],
1746 1746 ["+1.", "+1."],
1747 1747 [".1", ".1"],
1748 1748 # should not match non-numbers
1749 1749 ["1..", None],
1750 1750 ["..", None],
1751 1751 [".1.", None],
1752 1752 # should match after comma
1753 1753 [",1", "1"],
1754 1754 [", 1", "1"],
1755 1755 [", .1", ".1"],
1756 1756 [", +.1", "+.1"],
1757 1757 # should not match after trailing spaces
1758 1758 [".1 ", None],
1759 1759 # some complex cases
1760 1760 ["0b_0011_1111_0100_1110", "0b_0011_1111_0100_1110"],
1761 1761 ["0xdeadbeef", "0xdeadbeef"],
1762 1762 ["0b_1110_0101", "0b_1110_0101"],
1763 1763 # should not match if in an operation
1764 1764 ["1 + 1", None],
1765 1765 [", 1 + 1", None],
1766 1766 ],
1767 1767 )
1768 1768 def test_match_numeric_literal_for_dict_key(input, expected):
1769 1769 assert _match_number_in_dict_key_prefix(input) == expected
@@ -1,513 +1,513
1 1 # Copyright (c) IPython Development Team.
2 2 # Distributed under the terms of the Modified BSD License.
3 3
4 4 import json
5 5 import os
6 6 import warnings
7 7
8 8 from unittest import mock
9 9
10 10 import pytest
11 11
12 12 from IPython import display
13 13 from IPython.core.getipython import get_ipython
14 14 from IPython.utils.io import capture_output
15 15 from IPython.utils.tempdir import NamedFileInTemporaryDirectory
16 16 from IPython import paths as ipath
17 17 from IPython.testing.tools import AssertNotPrints
18 18
19 19 import IPython.testing.decorators as dec
20 20
21 21 def test_image_size():
22 22 """Simple test for display.Image(args, width=x,height=y)"""
23 23 thisurl = 'http://www.google.fr/images/srpr/logo3w.png'
24 24 img = display.Image(url=thisurl, width=200, height=200)
25 25 assert '<img src="%s" width="200" height="200"/>' % (thisurl) == img._repr_html_()
26 26 img = display.Image(url=thisurl, metadata={'width':200, 'height':200})
27 27 assert '<img src="%s" width="200" height="200"/>' % (thisurl) == img._repr_html_()
28 28 img = display.Image(url=thisurl, width=200)
29 29 assert '<img src="%s" width="200"/>' % (thisurl) == img._repr_html_()
30 30 img = display.Image(url=thisurl)
31 31 assert '<img src="%s"/>' % (thisurl) == img._repr_html_()
32 32 img = display.Image(url=thisurl, unconfined=True)
33 33 assert '<img src="%s" class="unconfined"/>' % (thisurl) == img._repr_html_()
34 34
35 35
36 36 def test_image_mimes():
37 37 fmt = get_ipython().display_formatter.format
38 38 for format in display.Image._ACCEPTABLE_EMBEDDINGS:
39 39 mime = display.Image._MIMETYPES[format]
40 40 img = display.Image(b'garbage', format=format)
41 41 data, metadata = fmt(img)
42 42 assert sorted(data) == sorted([mime, "text/plain"])
43 43
44 44
45 45 def test_geojson():
46 46
47 47 gj = display.GeoJSON(data={
48 48 "type": "Feature",
49 49 "geometry": {
50 50 "type": "Point",
51 51 "coordinates": [-81.327, 296.038]
52 52 },
53 53 "properties": {
54 54 "name": "Inca City"
55 55 }
56 56 },
57 57 url_template="http://s3-eu-west-1.amazonaws.com/whereonmars.cartodb.net/{basemap_id}/{z}/{x}/{y}.png",
58 58 layer_options={
59 59 "basemap_id": "celestia_mars-shaded-16k_global",
60 60 "attribution": "Celestia/praesepe",
61 61 "minZoom": 0,
62 62 "maxZoom": 18,
63 63 },
64 64 )
65 65 assert "<IPython.core.display.GeoJSON object>" == str(gj)
66 66
67 67
68 68 def test_retina_png():
69 69 here = os.path.dirname(__file__)
70 70 img = display.Image(os.path.join(here, "2x2.png"), retina=True)
71 71 assert img.height == 1
72 72 assert img.width == 1
73 73 data, md = img._repr_png_()
74 74 assert md["width"] == 1
75 75 assert md["height"] == 1
76 76
77 77
78 78 def test_embed_svg_url():
79 79 import gzip
80 80 from io import BytesIO
81 81 svg_data = b'<svg><circle x="0" y="0" r="1"/></svg>'
82 82 url = 'http://test.com/circle.svg'
83 83
84 84 gzip_svg = BytesIO()
85 85 with gzip.open(gzip_svg, 'wb') as fp:
86 86 fp.write(svg_data)
87 87 gzip_svg = gzip_svg.getvalue()
88 88
89 89 def mocked_urlopen(*args, **kwargs):
90 90 class MockResponse:
91 91 def __init__(self, svg):
92 92 self._svg_data = svg
93 93 self.headers = {'content-type': 'image/svg+xml'}
94 94
95 95 def read(self):
96 96 return self._svg_data
97 97
98 98 if args[0] == url:
99 99 return MockResponse(svg_data)
100 100 elif args[0] == url + "z":
101 101 ret = MockResponse(gzip_svg)
102 102 ret.headers["content-encoding"] = "gzip"
103 103 return ret
104 104 return MockResponse(None)
105 105
106 106 with mock.patch('urllib.request.urlopen', side_effect=mocked_urlopen):
107 107 svg = display.SVG(url=url)
108 108 assert svg._repr_svg_().startswith("<svg") is True
109 109 svg = display.SVG(url=url + "z")
110 110 assert svg._repr_svg_().startswith("<svg") is True
111 111
112 112
113 113 def test_retina_jpeg():
114 114 here = os.path.dirname(__file__)
115 115 img = display.Image(os.path.join(here, "2x2.jpg"), retina=True)
116 116 assert img.height == 1
117 117 assert img.width == 1
118 118 data, md = img._repr_jpeg_()
119 119 assert md["width"] == 1
120 120 assert md["height"] == 1
121 121
122 122
123 123 def test_base64image():
124 124 display.Image("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABAQMAAAAl21bKAAAAA1BMVEUAAACnej3aAAAAAWJLR0QAiAUdSAAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB94BCRQnOqNu0b4AAAAKSURBVAjXY2AAAAACAAHiIbwzAAAAAElFTkSuQmCC")
125 125
126 126 def test_image_filename_defaults():
127 127 '''test format constraint, and validity of jpeg and png'''
128 128 tpath = ipath.get_ipython_package_dir()
129 129 pytest.raises(
130 130 ValueError,
131 131 display.Image,
132 132 filename=os.path.join(tpath, "testing/tests/badformat.zip"),
133 133 embed=True,
134 134 )
135 135 pytest.raises(ValueError, display.Image)
136 136 pytest.raises(
137 137 ValueError,
138 138 display.Image,
139 139 data="this is not an image",
140 140 format="badformat",
141 141 embed=True,
142 142 )
143 # check boths paths to allow packages to test at build and install time
143 # check both paths to allow packages to test at build and install time
144 144 imgfile = os.path.join(tpath, 'core/tests/2x2.png')
145 145 img = display.Image(filename=imgfile)
146 146 assert "png" == img.format
147 147 assert img._repr_png_() is not None
148 148 img = display.Image(
149 149 filename=os.path.join(tpath, "testing/tests/logo.jpg"), embed=False
150 150 )
151 151 assert "jpeg" == img.format
152 152 assert img._repr_jpeg_() is None
153 153
154 154 def _get_inline_config():
155 155 from matplotlib_inline.config import InlineBackend
156 156 return InlineBackend.instance()
157 157
158 158
159 159 @dec.skip_without("matplotlib")
160 160 def test_set_matplotlib_close():
161 161 cfg = _get_inline_config()
162 162 cfg.close_figures = False
163 163 with pytest.deprecated_call():
164 164 display.set_matplotlib_close()
165 165 assert cfg.close_figures
166 166 with pytest.deprecated_call():
167 167 display.set_matplotlib_close(False)
168 168 assert not cfg.close_figures
169 169
170 170 _fmt_mime_map = {
171 171 'png': 'image/png',
172 172 'jpeg': 'image/jpeg',
173 173 'pdf': 'application/pdf',
174 174 'retina': 'image/png',
175 175 'svg': 'image/svg+xml',
176 176 }
177 177
178 178 @dec.skip_without('matplotlib')
179 179 def test_set_matplotlib_formats():
180 180 from matplotlib.figure import Figure
181 181 formatters = get_ipython().display_formatter.formatters
182 182 for formats in [
183 183 ('png',),
184 184 ('pdf', 'svg'),
185 185 ('jpeg', 'retina', 'png'),
186 186 (),
187 187 ]:
188 188 active_mimes = {_fmt_mime_map[fmt] for fmt in formats}
189 189 with pytest.deprecated_call():
190 190 display.set_matplotlib_formats(*formats)
191 191 for mime, f in formatters.items():
192 192 if mime in active_mimes:
193 193 assert Figure in f
194 194 else:
195 195 assert Figure not in f
196 196
197 197
198 198 @dec.skip_without("matplotlib")
199 199 def test_set_matplotlib_formats_kwargs():
200 200 from matplotlib.figure import Figure
201 201 ip = get_ipython()
202 202 cfg = _get_inline_config()
203 203 cfg.print_figure_kwargs.update(dict(foo='bar'))
204 204 kwargs = dict(dpi=150)
205 205 with pytest.deprecated_call():
206 206 display.set_matplotlib_formats("png", **kwargs)
207 207 formatter = ip.display_formatter.formatters["image/png"]
208 208 f = formatter.lookup_by_type(Figure)
209 209 formatter_kwargs = f.keywords
210 210 expected = kwargs
211 211 expected["base64"] = True
212 212 expected["fmt"] = "png"
213 213 expected.update(cfg.print_figure_kwargs)
214 214 assert formatter_kwargs == expected
215 215
216 216 def test_display_available():
217 217 """
218 218 Test that display is available without import
219 219
220 220 We don't really care if it's in builtin or anything else, but it should
221 221 always be available.
222 222 """
223 223 ip = get_ipython()
224 224 with AssertNotPrints('NameError'):
225 225 ip.run_cell('display')
226 226 try:
227 227 ip.run_cell('del display')
228 228 except NameError:
229 229 pass # it's ok, it might be in builtins
230 230 # even if deleted it should be back
231 231 with AssertNotPrints('NameError'):
232 232 ip.run_cell('display')
233 233
234 234 def test_textdisplayobj_pretty_repr():
235 235 p = display.Pretty("This is a simple test")
236 236 assert repr(p) == "<IPython.core.display.Pretty object>"
237 237 assert p.data == "This is a simple test"
238 238
239 239 p._show_mem_addr = True
240 240 assert repr(p) == object.__repr__(p)
241 241
242 242
243 243 def test_displayobject_repr():
244 244 h = display.HTML("<br />")
245 245 assert repr(h) == "<IPython.core.display.HTML object>"
246 246 h._show_mem_addr = True
247 247 assert repr(h) == object.__repr__(h)
248 248 h._show_mem_addr = False
249 249 assert repr(h) == "<IPython.core.display.HTML object>"
250 250
251 251 j = display.Javascript("")
252 252 assert repr(j) == "<IPython.core.display.Javascript object>"
253 253 j._show_mem_addr = True
254 254 assert repr(j) == object.__repr__(j)
255 255 j._show_mem_addr = False
256 256 assert repr(j) == "<IPython.core.display.Javascript object>"
257 257
258 258 @mock.patch('warnings.warn')
259 259 def test_encourage_iframe_over_html(m_warn):
260 260 display.HTML()
261 261 m_warn.assert_not_called()
262 262
263 263 display.HTML('<br />')
264 264 m_warn.assert_not_called()
265 265
266 266 display.HTML('<html><p>Lots of content here</p><iframe src="http://a.com"></iframe>')
267 267 m_warn.assert_not_called()
268 268
269 269 display.HTML('<iframe src="http://a.com"></iframe>')
270 270 m_warn.assert_called_with('Consider using IPython.display.IFrame instead')
271 271
272 272 m_warn.reset_mock()
273 273 display.HTML('<IFRAME SRC="http://a.com"></IFRAME>')
274 274 m_warn.assert_called_with('Consider using IPython.display.IFrame instead')
275 275
276 276 def test_progress():
277 277 p = display.ProgressBar(10)
278 278 assert "0/10" in repr(p)
279 279 p.html_width = "100%"
280 280 p.progress = 5
281 281 assert (
282 282 p._repr_html_() == "<progress style='width:100%' max='10' value='5'></progress>"
283 283 )
284 284
285 285
286 286 def test_progress_iter():
287 287 with capture_output(display=False) as captured:
288 288 for i in display.ProgressBar(5):
289 289 out = captured.stdout
290 290 assert "{0}/5".format(i) in out
291 291 out = captured.stdout
292 292 assert "5/5" in out
293 293
294 294
295 295 def test_json():
296 296 d = {'a': 5}
297 297 lis = [d]
298 298 metadata = [
299 299 {'expanded': False, 'root': 'root'},
300 300 {'expanded': True, 'root': 'root'},
301 301 {'expanded': False, 'root': 'custom'},
302 302 {'expanded': True, 'root': 'custom'},
303 303 ]
304 304 json_objs = [
305 305 display.JSON(d),
306 306 display.JSON(d, expanded=True),
307 307 display.JSON(d, root='custom'),
308 308 display.JSON(d, expanded=True, root='custom'),
309 309 ]
310 310 for j, md in zip(json_objs, metadata):
311 311 assert j._repr_json_() == (d, md)
312 312
313 313 with warnings.catch_warnings(record=True) as w:
314 314 warnings.simplefilter("always")
315 315 j = display.JSON(json.dumps(d))
316 316 assert len(w) == 1
317 317 assert j._repr_json_() == (d, metadata[0])
318 318
319 319 json_objs = [
320 320 display.JSON(lis),
321 321 display.JSON(lis, expanded=True),
322 322 display.JSON(lis, root='custom'),
323 323 display.JSON(lis, expanded=True, root='custom'),
324 324 ]
325 325 for j, md in zip(json_objs, metadata):
326 326 assert j._repr_json_() == (lis, md)
327 327
328 328 with warnings.catch_warnings(record=True) as w:
329 329 warnings.simplefilter("always")
330 330 j = display.JSON(json.dumps(lis))
331 331 assert len(w) == 1
332 332 assert j._repr_json_() == (lis, metadata[0])
333 333
334 334
335 335 def test_video_embedding():
336 336 """use a tempfile, with dummy-data, to ensure that video embedding doesn't crash"""
337 337 v = display.Video("http://ignored")
338 338 assert not v.embed
339 339 html = v._repr_html_()
340 340 assert 'src="data:' not in html
341 341 assert 'src="http://ignored"' in html
342 342
343 343 with pytest.raises(ValueError):
344 344 v = display.Video(b'abc')
345 345
346 346 with NamedFileInTemporaryDirectory('test.mp4') as f:
347 347 f.write(b'abc')
348 348 f.close()
349 349
350 350 v = display.Video(f.name)
351 351 assert not v.embed
352 352 html = v._repr_html_()
353 353 assert 'src="data:' not in html
354 354
355 355 v = display.Video(f.name, embed=True)
356 356 html = v._repr_html_()
357 357 assert 'src="data:video/mp4;base64,YWJj"' in html
358 358
359 359 v = display.Video(f.name, embed=True, mimetype='video/other')
360 360 html = v._repr_html_()
361 361 assert 'src="data:video/other;base64,YWJj"' in html
362 362
363 363 v = display.Video(b'abc', embed=True, mimetype='video/mp4')
364 364 html = v._repr_html_()
365 365 assert 'src="data:video/mp4;base64,YWJj"' in html
366 366
367 367 v = display.Video(u'YWJj', embed=True, mimetype='video/xyz')
368 368 html = v._repr_html_()
369 369 assert 'src="data:video/xyz;base64,YWJj"' in html
370 370
371 371 def test_html_metadata():
372 372 s = "<h1>Test</h1>"
373 373 h = display.HTML(s, metadata={"isolated": True})
374 374 assert h._repr_html_() == (s, {"isolated": True})
375 375
376 376
377 377 def test_display_id():
378 378 ip = get_ipython()
379 379 with mock.patch.object(ip.display_pub, 'publish') as pub:
380 380 handle = display.display('x')
381 381 assert handle is None
382 382 handle = display.display('y', display_id='secret')
383 383 assert isinstance(handle, display.DisplayHandle)
384 384 handle2 = display.display('z', display_id=True)
385 385 assert isinstance(handle2, display.DisplayHandle)
386 386 assert handle.display_id != handle2.display_id
387 387
388 388 assert pub.call_count == 3
389 389 args, kwargs = pub.call_args_list[0]
390 390 assert args == ()
391 391 assert kwargs == {
392 392 'data': {
393 393 'text/plain': repr('x')
394 394 },
395 395 'metadata': {},
396 396 }
397 397 args, kwargs = pub.call_args_list[1]
398 398 assert args == ()
399 399 assert kwargs == {
400 400 'data': {
401 401 'text/plain': repr('y')
402 402 },
403 403 'metadata': {},
404 404 'transient': {
405 405 'display_id': handle.display_id,
406 406 },
407 407 }
408 408 args, kwargs = pub.call_args_list[2]
409 409 assert args == ()
410 410 assert kwargs == {
411 411 'data': {
412 412 'text/plain': repr('z')
413 413 },
414 414 'metadata': {},
415 415 'transient': {
416 416 'display_id': handle2.display_id,
417 417 },
418 418 }
419 419
420 420
421 421 def test_update_display():
422 422 ip = get_ipython()
423 423 with mock.patch.object(ip.display_pub, 'publish') as pub:
424 424 with pytest.raises(TypeError):
425 425 display.update_display('x')
426 426 display.update_display('x', display_id='1')
427 427 display.update_display('y', display_id='2')
428 428 args, kwargs = pub.call_args_list[0]
429 429 assert args == ()
430 430 assert kwargs == {
431 431 'data': {
432 432 'text/plain': repr('x')
433 433 },
434 434 'metadata': {},
435 435 'transient': {
436 436 'display_id': '1',
437 437 },
438 438 'update': True,
439 439 }
440 440 args, kwargs = pub.call_args_list[1]
441 441 assert args == ()
442 442 assert kwargs == {
443 443 'data': {
444 444 'text/plain': repr('y')
445 445 },
446 446 'metadata': {},
447 447 'transient': {
448 448 'display_id': '2',
449 449 },
450 450 'update': True,
451 451 }
452 452
453 453
454 454 def test_display_handle():
455 455 ip = get_ipython()
456 456 handle = display.DisplayHandle()
457 457 assert isinstance(handle.display_id, str)
458 458 handle = display.DisplayHandle("my-id")
459 459 assert handle.display_id == "my-id"
460 460 with mock.patch.object(ip.display_pub, "publish") as pub:
461 461 handle.display("x")
462 462 handle.update("y")
463 463
464 464 args, kwargs = pub.call_args_list[0]
465 465 assert args == ()
466 466 assert kwargs == {
467 467 'data': {
468 468 'text/plain': repr('x')
469 469 },
470 470 'metadata': {},
471 471 'transient': {
472 472 'display_id': handle.display_id,
473 473 }
474 474 }
475 475 args, kwargs = pub.call_args_list[1]
476 476 assert args == ()
477 477 assert kwargs == {
478 478 'data': {
479 479 'text/plain': repr('y')
480 480 },
481 481 'metadata': {},
482 482 'transient': {
483 483 'display_id': handle.display_id,
484 484 },
485 485 'update': True,
486 486 }
487 487
488 488
489 489 def test_image_alt_tag():
490 490 """Simple test for display.Image(args, alt=x,)"""
491 491 thisurl = "http://example.com/image.png"
492 492 img = display.Image(url=thisurl, alt="an image")
493 493 assert '<img src="%s" alt="an image"/>' % (thisurl) == img._repr_html_()
494 494 img = display.Image(url=thisurl, unconfined=True, alt="an image")
495 495 assert (
496 496 '<img src="%s" class="unconfined" alt="an image"/>' % (thisurl)
497 497 == img._repr_html_()
498 498 )
499 499 img = display.Image(url=thisurl, alt='>"& <')
500 500 assert '<img src="%s" alt="&gt;&quot;&amp; &lt;"/>' % (thisurl) == img._repr_html_()
501 501
502 502 img = display.Image(url=thisurl, metadata={"alt": "an image"})
503 503 assert img.alt == "an image"
504 504 here = os.path.dirname(__file__)
505 505 img = display.Image(os.path.join(here, "2x2.png"), alt="an image")
506 506 assert img.alt == "an image"
507 507 _, md = img._repr_png_()
508 508 assert md["alt"] == "an image"
509 509
510 510
511 511 def test_image_bad_filename_raises_proper_exception():
512 512 with pytest.raises(FileNotFoundError):
513 513 display.Image("/this/file/does/not/exist/")._repr_png_()
@@ -1,1221 +1,1221
1 1 # -*- coding: utf-8 -*-
2 2 """Tests for the key interactiveshell module.
3 3
4 4 Historically the main classes in interactiveshell have been under-tested. This
5 5 module should grow as many single-method tests as possible to trap many of the
6 6 recurring bugs we seem to encounter with high-level interaction.
7 7 """
8 8
9 9 # Copyright (c) IPython Development Team.
10 10 # Distributed under the terms of the Modified BSD License.
11 11
12 12 import asyncio
13 13 import ast
14 14 import os
15 15 import signal
16 16 import shutil
17 17 import sys
18 18 import tempfile
19 19 import unittest
20 20 import pytest
21 21 from unittest import mock
22 22
23 23 from os.path import join
24 24
25 25 from IPython.core.error import InputRejected
26 26 from IPython.core.inputtransformer import InputTransformer
27 27 from IPython.core import interactiveshell
28 28 from IPython.core.oinspect import OInfo
29 29 from IPython.testing.decorators import (
30 30 skipif,
31 31 skip_win32,
32 32 onlyif_unicode_paths,
33 33 onlyif_cmds_exist,
34 34 skip_if_not_osx,
35 35 )
36 36 from IPython.testing import tools as tt
37 37 from IPython.utils.process import find_cmd
38 38
39 39 #-----------------------------------------------------------------------------
40 40 # Globals
41 41 #-----------------------------------------------------------------------------
42 42 # This is used by every single test, no point repeating it ad nauseam
43 43
44 44 #-----------------------------------------------------------------------------
45 45 # Tests
46 46 #-----------------------------------------------------------------------------
47 47
48 48 class DerivedInterrupt(KeyboardInterrupt):
49 49 pass
50 50
51 51 class InteractiveShellTestCase(unittest.TestCase):
52 52 def test_naked_string_cells(self):
53 53 """Test that cells with only naked strings are fully executed"""
54 54 # First, single-line inputs
55 55 ip.run_cell('"a"\n')
56 56 self.assertEqual(ip.user_ns['_'], 'a')
57 57 # And also multi-line cells
58 58 ip.run_cell('"""a\nb"""\n')
59 59 self.assertEqual(ip.user_ns['_'], 'a\nb')
60 60
61 61 def test_run_empty_cell(self):
62 62 """Just make sure we don't get a horrible error with a blank
63 63 cell of input. Yes, I did overlook that."""
64 64 old_xc = ip.execution_count
65 65 res = ip.run_cell('')
66 66 self.assertEqual(ip.execution_count, old_xc)
67 67 self.assertEqual(res.execution_count, None)
68 68
69 69 def test_run_cell_multiline(self):
70 70 """Multi-block, multi-line cells must execute correctly.
71 71 """
72 72 src = '\n'.join(["x=1",
73 73 "y=2",
74 74 "if 1:",
75 75 " x += 1",
76 76 " y += 1",])
77 77 res = ip.run_cell(src)
78 78 self.assertEqual(ip.user_ns['x'], 2)
79 79 self.assertEqual(ip.user_ns['y'], 3)
80 80 self.assertEqual(res.success, True)
81 81 self.assertEqual(res.result, None)
82 82
83 83 def test_multiline_string_cells(self):
84 84 "Code sprinkled with multiline strings should execute (GH-306)"
85 85 ip.run_cell('tmp=0')
86 86 self.assertEqual(ip.user_ns['tmp'], 0)
87 87 res = ip.run_cell('tmp=1;"""a\nb"""\n')
88 88 self.assertEqual(ip.user_ns['tmp'], 1)
89 89 self.assertEqual(res.success, True)
90 90 self.assertEqual(res.result, "a\nb")
91 91
92 92 def test_dont_cache_with_semicolon(self):
93 93 "Ending a line with semicolon should not cache the returned object (GH-307)"
94 94 oldlen = len(ip.user_ns['Out'])
95 95 for cell in ['1;', '1;1;']:
96 96 res = ip.run_cell(cell, store_history=True)
97 97 newlen = len(ip.user_ns['Out'])
98 98 self.assertEqual(oldlen, newlen)
99 99 self.assertIsNone(res.result)
100 100 i = 0
101 101 #also test the default caching behavior
102 102 for cell in ['1', '1;1']:
103 103 ip.run_cell(cell, store_history=True)
104 104 newlen = len(ip.user_ns['Out'])
105 105 i += 1
106 106 self.assertEqual(oldlen+i, newlen)
107 107
108 108 def test_syntax_error(self):
109 109 res = ip.run_cell("raise = 3")
110 110 self.assertIsInstance(res.error_before_exec, SyntaxError)
111 111
112 112 def test_open_standard_input_stream(self):
113 113 res = ip.run_cell("open(0)")
114 114 self.assertIsInstance(res.error_in_exec, ValueError)
115 115
116 116 def test_open_standard_output_stream(self):
117 117 res = ip.run_cell("open(1)")
118 118 self.assertIsInstance(res.error_in_exec, ValueError)
119 119
120 120 def test_open_standard_error_stream(self):
121 121 res = ip.run_cell("open(2)")
122 122 self.assertIsInstance(res.error_in_exec, ValueError)
123 123
124 124 def test_In_variable(self):
125 125 "Verify that In variable grows with user input (GH-284)"
126 126 oldlen = len(ip.user_ns['In'])
127 127 ip.run_cell('1;', store_history=True)
128 128 newlen = len(ip.user_ns['In'])
129 129 self.assertEqual(oldlen+1, newlen)
130 130 self.assertEqual(ip.user_ns['In'][-1],'1;')
131 131
132 132 def test_magic_names_in_string(self):
133 133 ip.run_cell('a = """\n%exit\n"""')
134 134 self.assertEqual(ip.user_ns['a'], '\n%exit\n')
135 135
136 136 def test_trailing_newline(self):
137 137 """test that running !(command) does not raise a SyntaxError"""
138 138 ip.run_cell('!(true)\n', False)
139 139 ip.run_cell('!(true)\n\n\n', False)
140 140
141 141 def test_gh_597(self):
142 142 """Pretty-printing lists of objects with non-ascii reprs may cause
143 143 problems."""
144 144 class Spam(object):
145 145 def __repr__(self):
146 146 return "\xe9"*50
147 147 import IPython.core.formatters
148 148 f = IPython.core.formatters.PlainTextFormatter()
149 149 f([Spam(), Spam()])
150 150
151 151 def test_future_flags(self):
152 152 """Check that future flags are used for parsing code (gh-777)"""
153 153 ip.run_cell('from __future__ import barry_as_FLUFL')
154 154 try:
155 155 ip.run_cell('prfunc_return_val = 1 <> 2')
156 156 assert 'prfunc_return_val' in ip.user_ns
157 157 finally:
158 158 # Reset compiler flags so we don't mess up other tests.
159 159 ip.compile.reset_compiler_flags()
160 160
161 161 def test_can_pickle(self):
162 162 "Can we pickle objects defined interactively (GH-29)"
163 163 ip = get_ipython()
164 164 ip.reset()
165 165 ip.run_cell(("class Mylist(list):\n"
166 166 " def __init__(self,x=[]):\n"
167 167 " list.__init__(self,x)"))
168 168 ip.run_cell("w=Mylist([1,2,3])")
169 169
170 170 from pickle import dumps
171 171
172 172 # We need to swap in our main module - this is only necessary
173 173 # inside the test framework, because IPython puts the interactive module
174 174 # in place (but the test framework undoes this).
175 175 _main = sys.modules['__main__']
176 176 sys.modules['__main__'] = ip.user_module
177 177 try:
178 178 res = dumps(ip.user_ns["w"])
179 179 finally:
180 180 sys.modules['__main__'] = _main
181 181 self.assertTrue(isinstance(res, bytes))
182 182
183 183 def test_global_ns(self):
184 184 "Code in functions must be able to access variables outside them."
185 185 ip = get_ipython()
186 186 ip.run_cell("a = 10")
187 187 ip.run_cell(("def f(x):\n"
188 188 " return x + a"))
189 189 ip.run_cell("b = f(12)")
190 190 self.assertEqual(ip.user_ns["b"], 22)
191 191
192 192 def test_bad_custom_tb(self):
193 193 """Check that InteractiveShell is protected from bad custom exception handlers"""
194 194 ip.set_custom_exc((IOError,), lambda etype,value,tb: 1/0)
195 195 self.assertEqual(ip.custom_exceptions, (IOError,))
196 196 with tt.AssertPrints("Custom TB Handler failed", channel='stderr'):
197 197 ip.run_cell(u'raise IOError("foo")')
198 198 self.assertEqual(ip.custom_exceptions, ())
199 199
200 200 def test_bad_custom_tb_return(self):
201 201 """Check that InteractiveShell is protected from bad return types in custom exception handlers"""
202 202 ip.set_custom_exc((NameError,),lambda etype,value,tb, tb_offset=None: 1)
203 203 self.assertEqual(ip.custom_exceptions, (NameError,))
204 204 with tt.AssertPrints("Custom TB Handler failed", channel='stderr'):
205 205 ip.run_cell(u'a=abracadabra')
206 206 self.assertEqual(ip.custom_exceptions, ())
207 207
208 208 def test_drop_by_id(self):
209 209 myvars = {"a":object(), "b":object(), "c": object()}
210 210 ip.push(myvars, interactive=False)
211 211 for name in myvars:
212 212 assert name in ip.user_ns, name
213 213 assert name in ip.user_ns_hidden, name
214 214 ip.user_ns['b'] = 12
215 215 ip.drop_by_id(myvars)
216 216 for name in ["a", "c"]:
217 217 assert name not in ip.user_ns, name
218 218 assert name not in ip.user_ns_hidden, name
219 219 assert ip.user_ns['b'] == 12
220 220 ip.reset()
221 221
222 222 def test_var_expand(self):
223 223 ip.user_ns['f'] = u'Ca\xf1o'
224 224 self.assertEqual(ip.var_expand(u'echo $f'), u'echo Ca\xf1o')
225 225 self.assertEqual(ip.var_expand(u'echo {f}'), u'echo Ca\xf1o')
226 226 self.assertEqual(ip.var_expand(u'echo {f[:-1]}'), u'echo Ca\xf1')
227 227 self.assertEqual(ip.var_expand(u'echo {1*2}'), u'echo 2')
228 228
229 229 self.assertEqual(ip.var_expand(u"grep x | awk '{print $1}'"), u"grep x | awk '{print $1}'")
230 230
231 231 ip.user_ns['f'] = b'Ca\xc3\xb1o'
232 232 # This should not raise any exception:
233 233 ip.var_expand(u'echo $f')
234 234
235 235 def test_var_expand_local(self):
236 236 """Test local variable expansion in !system and %magic calls"""
237 237 # !system
238 238 ip.run_cell(
239 239 "def test():\n"
240 240 ' lvar = "ttt"\n'
241 241 " ret = !echo {lvar}\n"
242 242 " return ret[0]\n"
243 243 )
244 244 res = ip.user_ns["test"]()
245 245 self.assertIn("ttt", res)
246 246
247 247 # %magic
248 248 ip.run_cell(
249 249 "def makemacro():\n"
250 250 ' macroname = "macro_var_expand_locals"\n'
251 251 " %macro {macroname} codestr\n"
252 252 )
253 253 ip.user_ns["codestr"] = "str(12)"
254 254 ip.run_cell("makemacro()")
255 255 self.assertIn("macro_var_expand_locals", ip.user_ns)
256 256
257 257 def test_var_expand_self(self):
258 258 """Test variable expansion with the name 'self', which was failing.
259 259
260 260 See https://github.com/ipython/ipython/issues/1878#issuecomment-7698218
261 261 """
262 262 ip.run_cell(
263 263 "class cTest:\n"
264 264 ' classvar="see me"\n'
265 265 " def test(self):\n"
266 266 " res = !echo Variable: {self.classvar}\n"
267 267 " return res[0]\n"
268 268 )
269 269 self.assertIn("see me", ip.user_ns["cTest"]().test())
270 270
271 271 def test_bad_var_expand(self):
272 272 """var_expand on invalid formats shouldn't raise"""
273 273 # SyntaxError
274 274 self.assertEqual(ip.var_expand(u"{'a':5}"), u"{'a':5}")
275 275 # NameError
276 276 self.assertEqual(ip.var_expand(u"{asdf}"), u"{asdf}")
277 277 # ZeroDivisionError
278 278 self.assertEqual(ip.var_expand(u"{1/0}"), u"{1/0}")
279 279
280 280 def test_silent_postexec(self):
281 281 """run_cell(silent=True) doesn't invoke pre/post_run_cell callbacks"""
282 282 pre_explicit = mock.Mock()
283 283 pre_always = mock.Mock()
284 284 post_explicit = mock.Mock()
285 285 post_always = mock.Mock()
286 286 all_mocks = [pre_explicit, pre_always, post_explicit, post_always]
287 287
288 288 ip.events.register('pre_run_cell', pre_explicit)
289 289 ip.events.register('pre_execute', pre_always)
290 290 ip.events.register('post_run_cell', post_explicit)
291 291 ip.events.register('post_execute', post_always)
292 292
293 293 try:
294 294 ip.run_cell("1", silent=True)
295 295 assert pre_always.called
296 296 assert not pre_explicit.called
297 297 assert post_always.called
298 298 assert not post_explicit.called
299 299 # double-check that non-silent exec did what we expected
300 300 # silent to avoid
301 301 ip.run_cell("1")
302 302 assert pre_explicit.called
303 303 assert post_explicit.called
304 304 info, = pre_explicit.call_args[0]
305 305 result, = post_explicit.call_args[0]
306 306 self.assertEqual(info, result.info)
307 307 # check that post hooks are always called
308 308 [m.reset_mock() for m in all_mocks]
309 309 ip.run_cell("syntax error")
310 310 assert pre_always.called
311 311 assert pre_explicit.called
312 312 assert post_always.called
313 313 assert post_explicit.called
314 314 info, = pre_explicit.call_args[0]
315 315 result, = post_explicit.call_args[0]
316 316 self.assertEqual(info, result.info)
317 317 finally:
318 318 # remove post-exec
319 319 ip.events.unregister('pre_run_cell', pre_explicit)
320 320 ip.events.unregister('pre_execute', pre_always)
321 321 ip.events.unregister('post_run_cell', post_explicit)
322 322 ip.events.unregister('post_execute', post_always)
323 323
324 324 def test_silent_noadvance(self):
325 325 """run_cell(silent=True) doesn't advance execution_count"""
326 326 ec = ip.execution_count
327 327 # silent should force store_history=False
328 328 ip.run_cell("1", store_history=True, silent=True)
329 329
330 330 self.assertEqual(ec, ip.execution_count)
331 331 # double-check that non-silent exec did what we expected
332 332 # silent to avoid
333 333 ip.run_cell("1", store_history=True)
334 334 self.assertEqual(ec+1, ip.execution_count)
335 335
336 336 def test_silent_nodisplayhook(self):
337 337 """run_cell(silent=True) doesn't trigger displayhook"""
338 338 d = dict(called=False)
339 339
340 340 trap = ip.display_trap
341 341 save_hook = trap.hook
342 342
343 343 def failing_hook(*args, **kwargs):
344 344 d['called'] = True
345 345
346 346 try:
347 347 trap.hook = failing_hook
348 348 res = ip.run_cell("1", silent=True)
349 349 self.assertFalse(d['called'])
350 350 self.assertIsNone(res.result)
351 351 # double-check that non-silent exec did what we expected
352 352 # silent to avoid
353 353 ip.run_cell("1")
354 354 self.assertTrue(d['called'])
355 355 finally:
356 356 trap.hook = save_hook
357 357
358 358 def test_ofind_line_magic(self):
359 359 from IPython.core.magic import register_line_magic
360 360
361 361 @register_line_magic
362 362 def lmagic(line):
363 363 "A line magic"
364 364
365 365 # Get info on line magic
366 366 lfind = ip._ofind("lmagic")
367 367 info = OInfo(
368 368 found=True,
369 369 isalias=False,
370 370 ismagic=True,
371 371 namespace="IPython internal",
372 372 obj=lmagic,
373 373 parent=None,
374 374 )
375 375 self.assertEqual(lfind, info)
376 376
377 377 def test_ofind_cell_magic(self):
378 378 from IPython.core.magic import register_cell_magic
379 379
380 380 @register_cell_magic
381 381 def cmagic(line, cell):
382 382 "A cell magic"
383 383
384 384 # Get info on cell magic
385 385 find = ip._ofind("cmagic")
386 386 info = OInfo(
387 387 found=True,
388 388 isalias=False,
389 389 ismagic=True,
390 390 namespace="IPython internal",
391 391 obj=cmagic,
392 392 parent=None,
393 393 )
394 394 self.assertEqual(find, info)
395 395
396 396 def test_ofind_property_with_error(self):
397 397 class A(object):
398 398 @property
399 399 def foo(self):
400 400 raise NotImplementedError() # pragma: no cover
401 401
402 402 a = A()
403 403
404 404 found = ip._ofind("a.foo", [("locals", locals())])
405 405 info = OInfo(
406 406 found=True,
407 407 isalias=False,
408 408 ismagic=False,
409 409 namespace="locals",
410 410 obj=A.foo,
411 411 parent=a,
412 412 )
413 413 self.assertEqual(found, info)
414 414
415 415 def test_ofind_multiple_attribute_lookups(self):
416 416 class A(object):
417 417 @property
418 418 def foo(self):
419 419 raise NotImplementedError() # pragma: no cover
420 420
421 421 a = A()
422 422 a.a = A()
423 423 a.a.a = A()
424 424
425 425 found = ip._ofind("a.a.a.foo", [("locals", locals())])
426 426 info = OInfo(
427 427 found=True,
428 428 isalias=False,
429 429 ismagic=False,
430 430 namespace="locals",
431 431 obj=A.foo,
432 432 parent=a.a.a,
433 433 )
434 434 self.assertEqual(found, info)
435 435
436 436 def test_ofind_slotted_attributes(self):
437 437 class A(object):
438 438 __slots__ = ['foo']
439 439 def __init__(self):
440 440 self.foo = 'bar'
441 441
442 442 a = A()
443 443 found = ip._ofind("a.foo", [("locals", locals())])
444 444 info = OInfo(
445 445 found=True,
446 446 isalias=False,
447 447 ismagic=False,
448 448 namespace="locals",
449 449 obj=a.foo,
450 450 parent=a,
451 451 )
452 452 self.assertEqual(found, info)
453 453
454 454 found = ip._ofind("a.bar", [("locals", locals())])
455 455 expected = OInfo(
456 456 found=False,
457 457 isalias=False,
458 458 ismagic=False,
459 459 namespace=None,
460 460 obj=None,
461 461 parent=a,
462 462 )
463 463 assert found == expected
464 464
465 465 def test_ofind_prefers_property_to_instance_level_attribute(self):
466 466 class A(object):
467 467 @property
468 468 def foo(self):
469 469 return 'bar'
470 470 a = A()
471 471 a.__dict__["foo"] = "baz"
472 472 self.assertEqual(a.foo, "bar")
473 473 found = ip._ofind("a.foo", [("locals", locals())])
474 474 self.assertIs(found.obj, A.foo)
475 475
476 476 def test_custom_syntaxerror_exception(self):
477 477 called = []
478 478 def my_handler(shell, etype, value, tb, tb_offset=None):
479 479 called.append(etype)
480 480 shell.showtraceback((etype, value, tb), tb_offset=tb_offset)
481 481
482 482 ip.set_custom_exc((SyntaxError,), my_handler)
483 483 try:
484 484 ip.run_cell("1f")
485 485 # Check that this was called, and only once.
486 486 self.assertEqual(called, [SyntaxError])
487 487 finally:
488 488 # Reset the custom exception hook
489 489 ip.set_custom_exc((), None)
490 490
491 491 def test_custom_exception(self):
492 492 called = []
493 493 def my_handler(shell, etype, value, tb, tb_offset=None):
494 494 called.append(etype)
495 495 shell.showtraceback((etype, value, tb), tb_offset=tb_offset)
496 496
497 497 ip.set_custom_exc((ValueError,), my_handler)
498 498 try:
499 499 res = ip.run_cell("raise ValueError('test')")
500 500 # Check that this was called, and only once.
501 501 self.assertEqual(called, [ValueError])
502 502 # Check that the error is on the result object
503 503 self.assertIsInstance(res.error_in_exec, ValueError)
504 504 finally:
505 505 # Reset the custom exception hook
506 506 ip.set_custom_exc((), None)
507 507
508 508 @mock.patch("builtins.print")
509 509 def test_showtraceback_with_surrogates(self, mocked_print):
510 510 values = []
511 511
512 512 def mock_print_func(value, sep=" ", end="\n", file=sys.stdout, flush=False):
513 513 values.append(value)
514 514 if value == chr(0xD8FF):
515 515 raise UnicodeEncodeError("utf-8", chr(0xD8FF), 0, 1, "")
516 516
517 517 # mock builtins.print
518 518 mocked_print.side_effect = mock_print_func
519 519
520 520 # ip._showtraceback() is replaced in globalipapp.py.
521 521 # Call original method to test.
522 522 interactiveshell.InteractiveShell._showtraceback(ip, None, None, chr(0xD8FF))
523 523
524 524 self.assertEqual(mocked_print.call_count, 2)
525 525 self.assertEqual(values, [chr(0xD8FF), "\\ud8ff"])
526 526
527 527 def test_mktempfile(self):
528 528 filename = ip.mktempfile()
529 529 # Check that we can open the file again on Windows
530 530 with open(filename, "w", encoding="utf-8") as f:
531 531 f.write("abc")
532 532
533 533 filename = ip.mktempfile(data="blah")
534 534 with open(filename, "r", encoding="utf-8") as f:
535 535 self.assertEqual(f.read(), "blah")
536 536
537 537 def test_new_main_mod(self):
538 538 # Smoketest to check that this accepts a unicode module name
539 539 name = u'jiefmw'
540 540 mod = ip.new_main_mod(u'%s.py' % name, name)
541 541 self.assertEqual(mod.__name__, name)
542 542
543 543 def test_get_exception_only(self):
544 544 try:
545 545 raise KeyboardInterrupt
546 546 except KeyboardInterrupt:
547 547 msg = ip.get_exception_only()
548 548 self.assertEqual(msg, 'KeyboardInterrupt\n')
549 549
550 550 try:
551 551 raise DerivedInterrupt("foo")
552 552 except KeyboardInterrupt:
553 553 msg = ip.get_exception_only()
554 554 self.assertEqual(msg, 'IPython.core.tests.test_interactiveshell.DerivedInterrupt: foo\n')
555 555
556 556 def test_inspect_text(self):
557 557 ip.run_cell('a = 5')
558 558 text = ip.object_inspect_text('a')
559 559 self.assertIsInstance(text, str)
560 560
561 561 def test_last_execution_result(self):
562 562 """ Check that last execution result gets set correctly (GH-10702) """
563 563 result = ip.run_cell('a = 5; a')
564 564 self.assertTrue(ip.last_execution_succeeded)
565 565 self.assertEqual(ip.last_execution_result.result, 5)
566 566
567 567 result = ip.run_cell('a = x_invalid_id_x')
568 568 self.assertFalse(ip.last_execution_succeeded)
569 569 self.assertFalse(ip.last_execution_result.success)
570 570 self.assertIsInstance(ip.last_execution_result.error_in_exec, NameError)
571 571
572 572 def test_reset_aliasing(self):
573 573 """ Check that standard posix aliases work after %reset. """
574 574 if os.name != 'posix':
575 575 return
576 576
577 577 ip.reset()
578 578 for cmd in ('clear', 'more', 'less', 'man'):
579 579 res = ip.run_cell('%' + cmd)
580 580 self.assertEqual(res.success, True)
581 581
582 582
583 583 @pytest.mark.skipif(
584 584 sys.implementation.name == "pypy"
585 585 and ((7, 3, 13) < sys.implementation.version < (7, 3, 16)),
586 586 reason="Unicode issues with scandir on PyPy, see https://github.com/pypy/pypy/issues/4860",
587 587 )
588 588 class TestSafeExecfileNonAsciiPath(unittest.TestCase):
589 589 @onlyif_unicode_paths
590 590 def setUp(self):
591 591 self.BASETESTDIR = tempfile.mkdtemp()
592 592 self.TESTDIR = join(self.BASETESTDIR, u"Γ₯Àâ")
593 593 os.mkdir(self.TESTDIR)
594 594 with open(
595 595 join(self.TESTDIR, "Γ₯Àâtestscript.py"), "w", encoding="utf-8"
596 596 ) as sfile:
597 597 sfile.write("pass\n")
598 598 self.oldpath = os.getcwd()
599 599 os.chdir(self.TESTDIR)
600 600 self.fname = u"Γ₯Àâtestscript.py"
601 601
602 602 def tearDown(self):
603 603 os.chdir(self.oldpath)
604 604 shutil.rmtree(self.BASETESTDIR)
605 605
606 606 @onlyif_unicode_paths
607 607 def test_1(self):
608 608 """Test safe_execfile with non-ascii path
609 609 """
610 610 ip.safe_execfile(self.fname, {}, raise_exceptions=True)
611 611
612 612 class ExitCodeChecks(tt.TempFileMixin):
613 613
614 614 def setUp(self):
615 615 self.system = ip.system_raw
616 616
617 617 def test_exit_code_ok(self):
618 618 self.system('exit 0')
619 619 self.assertEqual(ip.user_ns['_exit_code'], 0)
620 620
621 621 def test_exit_code_error(self):
622 622 self.system('exit 1')
623 623 self.assertEqual(ip.user_ns['_exit_code'], 1)
624 624
625 625 @skipif(not hasattr(signal, 'SIGALRM'))
626 626 def test_exit_code_signal(self):
627 627 self.mktmp("import signal, time\n"
628 628 "signal.setitimer(signal.ITIMER_REAL, 0.1)\n"
629 629 "time.sleep(1)\n")
630 630 self.system("%s %s" % (sys.executable, self.fname))
631 631 self.assertEqual(ip.user_ns['_exit_code'], -signal.SIGALRM)
632 632
633 633 @onlyif_cmds_exist("csh")
634 634 def test_exit_code_signal_csh(self): # pragma: no cover
635 635 SHELL = os.environ.get("SHELL", None)
636 636 os.environ["SHELL"] = find_cmd("csh")
637 637 try:
638 638 self.test_exit_code_signal()
639 639 finally:
640 640 if SHELL is not None:
641 641 os.environ['SHELL'] = SHELL
642 642 else:
643 643 del os.environ['SHELL']
644 644
645 645
646 646 class TestSystemRaw(ExitCodeChecks):
647 647
648 648 def setUp(self):
649 649 super().setUp()
650 650 self.system = ip.system_raw
651 651
652 652 @onlyif_unicode_paths
653 653 def test_1(self):
654 654 """Test system_raw with non-ascii cmd
655 655 """
656 656 cmd = u'''python -c "'Γ₯Àâ'" '''
657 657 ip.system_raw(cmd)
658 658
659 659 @mock.patch('subprocess.call', side_effect=KeyboardInterrupt)
660 660 @mock.patch('os.system', side_effect=KeyboardInterrupt)
661 661 def test_control_c(self, *mocks):
662 662 try:
663 self.system("sleep 1 # wont happen")
663 self.system("sleep 1 # won't happen")
664 664 except KeyboardInterrupt: # pragma: no cove
665 665 self.fail(
666 666 "system call should intercept "
667 667 "keyboard interrupt from subprocess.call"
668 668 )
669 669 self.assertEqual(ip.user_ns["_exit_code"], -signal.SIGINT)
670 670
671 671
672 672 @pytest.mark.parametrize("magic_cmd", ["pip", "conda", "cd"])
673 673 def test_magic_warnings(magic_cmd):
674 674 if sys.platform == "win32":
675 675 to_mock = "os.system"
676 676 expected_arg, expected_kwargs = magic_cmd, dict()
677 677 else:
678 678 to_mock = "subprocess.call"
679 679 expected_arg, expected_kwargs = magic_cmd, dict(
680 680 shell=True, executable=os.environ.get("SHELL", None)
681 681 )
682 682
683 683 with mock.patch(to_mock, return_value=0) as mock_sub:
684 684 with pytest.warns(Warning, match=r"You executed the system command"):
685 685 ip.system_raw(magic_cmd)
686 686 mock_sub.assert_called_once_with(expected_arg, **expected_kwargs)
687 687
688 688
689 689 # TODO: Exit codes are currently ignored on Windows.
690 690 class TestSystemPipedExitCode(ExitCodeChecks):
691 691
692 692 def setUp(self):
693 693 super().setUp()
694 694 self.system = ip.system_piped
695 695
696 696 @skip_win32
697 697 def test_exit_code_ok(self):
698 698 ExitCodeChecks.test_exit_code_ok(self)
699 699
700 700 @skip_win32
701 701 def test_exit_code_error(self):
702 702 ExitCodeChecks.test_exit_code_error(self)
703 703
704 704 @skip_win32
705 705 def test_exit_code_signal(self):
706 706 ExitCodeChecks.test_exit_code_signal(self)
707 707
708 708 class TestModules(tt.TempFileMixin):
709 709 def test_extraneous_loads(self):
710 710 """Test we're not loading modules on startup that we shouldn't.
711 711 """
712 712 self.mktmp("import sys\n"
713 713 "print('numpy' in sys.modules)\n"
714 714 "print('ipyparallel' in sys.modules)\n"
715 715 "print('ipykernel' in sys.modules)\n"
716 716 )
717 717 out = "False\nFalse\nFalse\n"
718 718 tt.ipexec_validate(self.fname, out)
719 719
720 720 class Negator(ast.NodeTransformer):
721 721 """Negates all number literals in an AST."""
722 722
723 723 def visit_Num(self, node):
724 724 node.n = -node.n
725 725 return node
726 726
727 727 def visit_Constant(self, node):
728 728 if isinstance(node.value, int):
729 729 return self.visit_Num(node)
730 730 return node
731 731
732 732 class TestAstTransform(unittest.TestCase):
733 733 def setUp(self):
734 734 self.negator = Negator()
735 735 ip.ast_transformers.append(self.negator)
736 736
737 737 def tearDown(self):
738 738 ip.ast_transformers.remove(self.negator)
739 739
740 740 def test_non_int_const(self):
741 741 with tt.AssertPrints("hello"):
742 742 ip.run_cell('print("hello")')
743 743
744 744 def test_run_cell(self):
745 745 with tt.AssertPrints("-34"):
746 746 ip.run_cell("print(12 + 22)")
747 747
748 748 # A named reference to a number shouldn't be transformed.
749 749 ip.user_ns["n"] = 55
750 750 with tt.AssertNotPrints("-55"):
751 751 ip.run_cell("print(n)")
752 752
753 753 def test_timeit(self):
754 754 called = set()
755 755 def f(x):
756 756 called.add(x)
757 757 ip.push({'f':f})
758 758
759 759 with tt.AssertPrints("std. dev. of"):
760 760 ip.run_line_magic("timeit", "-n1 f(1)")
761 761 self.assertEqual(called, {-1})
762 762 called.clear()
763 763
764 764 with tt.AssertPrints("std. dev. of"):
765 765 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
766 766 self.assertEqual(called, {-2, -3})
767 767
768 768 def test_time(self):
769 769 called = []
770 770 def f(x):
771 771 called.append(x)
772 772 ip.push({'f':f})
773 773
774 774 # Test with an expression
775 775 with tt.AssertPrints("Wall time: "):
776 776 ip.run_line_magic("time", "f(5+9)")
777 777 self.assertEqual(called, [-14])
778 778 called[:] = []
779 779
780 780 # Test with a statement (different code path)
781 781 with tt.AssertPrints("Wall time: "):
782 782 ip.run_line_magic("time", "a = f(-3 + -2)")
783 783 self.assertEqual(called, [5])
784 784
785 785 def test_macro(self):
786 786 ip.push({'a':10})
787 787 # The AST transformation makes this do a+=-1
788 788 ip.define_macro("amacro", "a+=1\nprint(a)")
789 789
790 790 with tt.AssertPrints("9"):
791 791 ip.run_cell("amacro")
792 792 with tt.AssertPrints("8"):
793 793 ip.run_cell("amacro")
794 794
795 795 class TestMiscTransform(unittest.TestCase):
796 796
797 797
798 798 def test_transform_only_once(self):
799 799 cleanup = 0
800 800 line_t = 0
801 801 def count_cleanup(lines):
802 802 nonlocal cleanup
803 803 cleanup += 1
804 804 return lines
805 805
806 806 def count_line_t(lines):
807 807 nonlocal line_t
808 808 line_t += 1
809 809 return lines
810 810
811 811 ip.input_transformer_manager.cleanup_transforms.append(count_cleanup)
812 812 ip.input_transformer_manager.line_transforms.append(count_line_t)
813 813
814 814 ip.run_cell('1')
815 815
816 816 assert cleanup == 1
817 817 assert line_t == 1
818 818
819 819 class IntegerWrapper(ast.NodeTransformer):
820 820 """Wraps all integers in a call to Integer()"""
821 821
822 822 # for Python 3.7 and earlier
823 823
824 824 # for Python 3.7 and earlier
825 825 def visit_Num(self, node):
826 826 if isinstance(node.n, int):
827 827 return ast.Call(func=ast.Name(id='Integer', ctx=ast.Load()),
828 828 args=[node], keywords=[])
829 829 return node
830 830
831 831 # For Python 3.8+
832 832 def visit_Constant(self, node):
833 833 if isinstance(node.value, int):
834 834 return self.visit_Num(node)
835 835 return node
836 836
837 837
838 838 class TestAstTransform2(unittest.TestCase):
839 839 def setUp(self):
840 840 self.intwrapper = IntegerWrapper()
841 841 ip.ast_transformers.append(self.intwrapper)
842 842
843 843 self.calls = []
844 844 def Integer(*args):
845 845 self.calls.append(args)
846 846 return args
847 847 ip.push({"Integer": Integer})
848 848
849 849 def tearDown(self):
850 850 ip.ast_transformers.remove(self.intwrapper)
851 851 del ip.user_ns['Integer']
852 852
853 853 def test_run_cell(self):
854 854 ip.run_cell("n = 2")
855 855 self.assertEqual(self.calls, [(2,)])
856 856
857 857 # This shouldn't throw an error
858 858 ip.run_cell("o = 2.0")
859 859 self.assertEqual(ip.user_ns['o'], 2.0)
860 860
861 861 def test_run_cell_non_int(self):
862 862 ip.run_cell("n = 'a'")
863 863 assert self.calls == []
864 864
865 865 def test_timeit(self):
866 866 called = set()
867 867 def f(x):
868 868 called.add(x)
869 869 ip.push({'f':f})
870 870
871 871 with tt.AssertPrints("std. dev. of"):
872 872 ip.run_line_magic("timeit", "-n1 f(1)")
873 873 self.assertEqual(called, {(1,)})
874 874 called.clear()
875 875
876 876 with tt.AssertPrints("std. dev. of"):
877 877 ip.run_cell_magic("timeit", "-n1 f(2)", "f(3)")
878 878 self.assertEqual(called, {(2,), (3,)})
879 879
880 880 class ErrorTransformer(ast.NodeTransformer):
881 881 """Throws an error when it sees a number."""
882 882
883 883 def visit_Constant(self, node):
884 884 if isinstance(node.value, int):
885 885 raise ValueError("test")
886 886 return node
887 887
888 888
889 889 class TestAstTransformError(unittest.TestCase):
890 890 def test_unregistering(self):
891 891 err_transformer = ErrorTransformer()
892 892 ip.ast_transformers.append(err_transformer)
893 893
894 894 with self.assertWarnsRegex(UserWarning, "It will be unregistered"):
895 895 ip.run_cell("1 + 2")
896 896
897 897 # This should have been removed.
898 898 self.assertNotIn(err_transformer, ip.ast_transformers)
899 899
900 900
901 901 class StringRejector(ast.NodeTransformer):
902 902 """Throws an InputRejected when it sees a string literal.
903 903
904 904 Used to verify that NodeTransformers can signal that a piece of code should
905 905 not be executed by throwing an InputRejected.
906 906 """
907 907
908 908 def visit_Constant(self, node):
909 909 if isinstance(node.value, str):
910 910 raise InputRejected("test")
911 911 return node
912 912
913 913
914 914 class TestAstTransformInputRejection(unittest.TestCase):
915 915
916 916 def setUp(self):
917 917 self.transformer = StringRejector()
918 918 ip.ast_transformers.append(self.transformer)
919 919
920 920 def tearDown(self):
921 921 ip.ast_transformers.remove(self.transformer)
922 922
923 923 def test_input_rejection(self):
924 924 """Check that NodeTransformers can reject input."""
925 925
926 926 expect_exception_tb = tt.AssertPrints("InputRejected: test")
927 927 expect_no_cell_output = tt.AssertNotPrints("'unsafe'", suppress=False)
928 928
929 929 # Run the same check twice to verify that the transformer is not
930 930 # disabled after raising.
931 931 with expect_exception_tb, expect_no_cell_output:
932 932 ip.run_cell("'unsafe'")
933 933
934 934 with expect_exception_tb, expect_no_cell_output:
935 935 res = ip.run_cell("'unsafe'")
936 936
937 937 self.assertIsInstance(res.error_before_exec, InputRejected)
938 938
939 939 def test__IPYTHON__():
940 940 # This shouldn't raise a NameError, that's all
941 941 __IPYTHON__
942 942
943 943
944 944 class DummyRepr(object):
945 945 def __repr__(self):
946 946 return "DummyRepr"
947 947
948 948 def _repr_html_(self):
949 949 return "<b>dummy</b>"
950 950
951 951 def _repr_javascript_(self):
952 952 return "console.log('hi');", {'key': 'value'}
953 953
954 954
955 955 def test_user_variables():
956 956 # enable all formatters
957 957 ip.display_formatter.active_types = ip.display_formatter.format_types
958 958
959 959 ip.user_ns['dummy'] = d = DummyRepr()
960 960 keys = {'dummy', 'doesnotexist'}
961 961 r = ip.user_expressions({ key:key for key in keys})
962 962
963 963 assert keys == set(r.keys())
964 964 dummy = r["dummy"]
965 965 assert {"status", "data", "metadata"} == set(dummy.keys())
966 966 assert dummy["status"] == "ok"
967 967 data = dummy["data"]
968 968 metadata = dummy["metadata"]
969 969 assert data.get("text/html") == d._repr_html_()
970 970 js, jsmd = d._repr_javascript_()
971 971 assert data.get("application/javascript") == js
972 972 assert metadata.get("application/javascript") == jsmd
973 973
974 974 dne = r["doesnotexist"]
975 975 assert dne["status"] == "error"
976 976 assert dne["ename"] == "NameError"
977 977
978 978 # back to text only
979 979 ip.display_formatter.active_types = ['text/plain']
980 980
981 981 def test_user_expression():
982 982 # enable all formatters
983 983 ip.display_formatter.active_types = ip.display_formatter.format_types
984 984 query = {
985 985 'a' : '1 + 2',
986 986 'b' : '1/0',
987 987 }
988 988 r = ip.user_expressions(query)
989 989 import pprint
990 990 pprint.pprint(r)
991 991 assert set(r.keys()) == set(query.keys())
992 992 a = r["a"]
993 993 assert {"status", "data", "metadata"} == set(a.keys())
994 994 assert a["status"] == "ok"
995 995 data = a["data"]
996 996 metadata = a["metadata"]
997 997 assert data.get("text/plain") == "3"
998 998
999 999 b = r["b"]
1000 1000 assert b["status"] == "error"
1001 1001 assert b["ename"] == "ZeroDivisionError"
1002 1002
1003 1003 # back to text only
1004 1004 ip.display_formatter.active_types = ['text/plain']
1005 1005
1006 1006
1007 1007 class TestSyntaxErrorTransformer(unittest.TestCase):
1008 1008 """Check that SyntaxError raised by an input transformer is handled by run_cell()"""
1009 1009
1010 1010 @staticmethod
1011 1011 def transformer(lines):
1012 1012 for line in lines:
1013 1013 pos = line.find('syntaxerror')
1014 1014 if pos >= 0:
1015 1015 e = SyntaxError('input contains "syntaxerror"')
1016 1016 e.text = line
1017 1017 e.offset = pos + 1
1018 1018 raise e
1019 1019 return lines
1020 1020
1021 1021 def setUp(self):
1022 1022 ip.input_transformers_post.append(self.transformer)
1023 1023
1024 1024 def tearDown(self):
1025 1025 ip.input_transformers_post.remove(self.transformer)
1026 1026
1027 1027 def test_syntaxerror_input_transformer(self):
1028 1028 with tt.AssertPrints('1234'):
1029 1029 ip.run_cell('1234')
1030 1030 with tt.AssertPrints('SyntaxError: invalid syntax'):
1031 1031 ip.run_cell('1 2 3') # plain python syntax error
1032 1032 with tt.AssertPrints('SyntaxError: input contains "syntaxerror"'):
1033 1033 ip.run_cell('2345 # syntaxerror') # input transformer syntax error
1034 1034 with tt.AssertPrints('3456'):
1035 1035 ip.run_cell('3456')
1036 1036
1037 1037
1038 1038 class TestWarningSuppression(unittest.TestCase):
1039 1039 def test_warning_suppression(self):
1040 1040 ip.run_cell("import warnings")
1041 1041 try:
1042 1042 with self.assertWarnsRegex(UserWarning, "asdf"):
1043 1043 ip.run_cell("warnings.warn('asdf')")
1044 1044 # Here's the real test -- if we run that again, we should get the
1045 1045 # warning again. Traditionally, each warning was only issued once per
1046 1046 # IPython session (approximately), even if the user typed in new and
1047 1047 # different code that should have also triggered the warning, leading
1048 1048 # to much confusion.
1049 1049 with self.assertWarnsRegex(UserWarning, "asdf"):
1050 1050 ip.run_cell("warnings.warn('asdf')")
1051 1051 finally:
1052 1052 ip.run_cell("del warnings")
1053 1053
1054 1054
1055 1055 def test_deprecation_warning(self):
1056 1056 ip.run_cell("""
1057 1057 import warnings
1058 1058 def wrn():
1059 1059 warnings.warn(
1060 1060 "I AM A WARNING",
1061 1061 DeprecationWarning
1062 1062 )
1063 1063 """)
1064 1064 try:
1065 1065 with self.assertWarnsRegex(DeprecationWarning, "I AM A WARNING"):
1066 1066 ip.run_cell("wrn()")
1067 1067 finally:
1068 1068 ip.run_cell("del warnings")
1069 1069 ip.run_cell("del wrn")
1070 1070
1071 1071
1072 1072 class TestImportNoDeprecate(tt.TempFileMixin):
1073 1073
1074 1074 def setUp(self):
1075 1075 """Make a valid python temp file."""
1076 1076 self.mktmp("""
1077 1077 import warnings
1078 1078 def wrn():
1079 1079 warnings.warn(
1080 1080 "I AM A WARNING",
1081 1081 DeprecationWarning
1082 1082 )
1083 1083 """)
1084 1084 super().setUp()
1085 1085
1086 1086 def test_no_dep(self):
1087 1087 """
1088 1088 No deprecation warning should be raised from imported functions
1089 1089 """
1090 1090 ip.run_cell("from {} import wrn".format(self.fname))
1091 1091
1092 1092 with tt.AssertNotPrints("I AM A WARNING"):
1093 1093 ip.run_cell("wrn()")
1094 1094 ip.run_cell("del wrn")
1095 1095
1096 1096
1097 1097 def test_custom_exc_count():
1098 1098 hook = mock.Mock(return_value=None)
1099 1099 ip.set_custom_exc((SyntaxError,), hook)
1100 1100 before = ip.execution_count
1101 1101 ip.run_cell("def foo()", store_history=True)
1102 1102 # restore default excepthook
1103 1103 ip.set_custom_exc((), None)
1104 1104 assert hook.call_count == 1
1105 1105 assert ip.execution_count == before + 1
1106 1106
1107 1107
1108 1108 def test_run_cell_async():
1109 1109 ip.run_cell("import asyncio")
1110 1110 coro = ip.run_cell_async("await asyncio.sleep(0.01)\n5")
1111 1111 assert asyncio.iscoroutine(coro)
1112 1112 loop = asyncio.new_event_loop()
1113 1113 result = loop.run_until_complete(coro)
1114 1114 assert isinstance(result, interactiveshell.ExecutionResult)
1115 1115 assert result.result == 5
1116 1116
1117 1117
1118 1118 def test_run_cell_await():
1119 1119 ip.run_cell("import asyncio")
1120 1120 result = ip.run_cell("await asyncio.sleep(0.01); 10")
1121 1121 assert ip.user_ns["_"] == 10
1122 1122
1123 1123
1124 1124 def test_run_cell_asyncio_run():
1125 1125 ip.run_cell("import asyncio")
1126 1126 result = ip.run_cell("await asyncio.sleep(0.01); 1")
1127 1127 assert ip.user_ns["_"] == 1
1128 1128 result = ip.run_cell("asyncio.run(asyncio.sleep(0.01)); 2")
1129 1129 assert ip.user_ns["_"] == 2
1130 1130 result = ip.run_cell("await asyncio.sleep(0.01); 3")
1131 1131 assert ip.user_ns["_"] == 3
1132 1132
1133 1133
1134 1134 def test_should_run_async():
1135 1135 assert not ip.should_run_async("a = 5", transformed_cell="a = 5")
1136 1136 assert ip.should_run_async("await x", transformed_cell="await x")
1137 1137 assert ip.should_run_async(
1138 1138 "import asyncio; await asyncio.sleep(1)",
1139 1139 transformed_cell="import asyncio; await asyncio.sleep(1)",
1140 1140 )
1141 1141
1142 1142
1143 1143 def test_set_custom_completer():
1144 1144 num_completers = len(ip.Completer.matchers)
1145 1145
1146 1146 def foo(*args, **kwargs):
1147 1147 return "I'm a completer!"
1148 1148
1149 1149 ip.set_custom_completer(foo, 0)
1150 1150
1151 1151 # check that we've really added a new completer
1152 1152 assert len(ip.Completer.matchers) == num_completers + 1
1153 1153
1154 1154 # check that the first completer is the function we defined
1155 1155 assert ip.Completer.matchers[0]() == "I'm a completer!"
1156 1156
1157 1157 # clean up
1158 1158 ip.Completer.custom_matchers.pop()
1159 1159
1160 1160
1161 1161 class TestShowTracebackAttack(unittest.TestCase):
1162 1162 """Test that the interactive shell is resilient against the client attack of
1163 1163 manipulating the showtracebacks method. These attacks shouldn't result in an
1164 1164 unhandled exception in the kernel."""
1165 1165
1166 1166 def setUp(self):
1167 1167 self.orig_showtraceback = interactiveshell.InteractiveShell.showtraceback
1168 1168
1169 1169 def tearDown(self):
1170 1170 interactiveshell.InteractiveShell.showtraceback = self.orig_showtraceback
1171 1171
1172 1172 def test_set_show_tracebacks_none(self):
1173 1173 """Test the case of the client setting showtracebacks to None"""
1174 1174
1175 1175 result = ip.run_cell(
1176 1176 """
1177 1177 import IPython.core.interactiveshell
1178 1178 IPython.core.interactiveshell.InteractiveShell.showtraceback = None
1179 1179
1180 1180 assert False, "This should not raise an exception"
1181 1181 """
1182 1182 )
1183 1183 print(result)
1184 1184
1185 1185 assert result.result is None
1186 1186 assert isinstance(result.error_in_exec, TypeError)
1187 1187 assert str(result.error_in_exec) == "'NoneType' object is not callable"
1188 1188
1189 1189 def test_set_show_tracebacks_noop(self):
1190 1190 """Test the case of the client setting showtracebacks to a no op lambda"""
1191 1191
1192 1192 result = ip.run_cell(
1193 1193 """
1194 1194 import IPython.core.interactiveshell
1195 1195 IPython.core.interactiveshell.InteractiveShell.showtraceback = lambda *args, **kwargs: None
1196 1196
1197 1197 assert False, "This should not raise an exception"
1198 1198 """
1199 1199 )
1200 1200 print(result)
1201 1201
1202 1202 assert result.result is None
1203 1203 assert isinstance(result.error_in_exec, AssertionError)
1204 1204 assert str(result.error_in_exec) == "This should not raise an exception"
1205 1205
1206 1206
1207 1207 @skip_if_not_osx
1208 1208 def test_enable_gui_osx():
1209 1209 simple_prompt = ip.simple_prompt
1210 1210 ip.simple_prompt = False
1211 1211
1212 1212 ip.enable_gui("osx")
1213 1213 assert ip.active_eventloop == "osx"
1214 1214 ip.enable_gui()
1215 1215
1216 1216 # The following line fails for IPython <= 8.25.0
1217 1217 ip.enable_gui("macosx")
1218 1218 assert ip.active_eventloop == "osx"
1219 1219 ip.enable_gui()
1220 1220
1221 1221 ip.simple_prompt = simple_prompt
@@ -1,626 +1,626
1 1 # encoding: utf-8
2 2 """Tests for code execution (%run and related), which is particularly tricky.
3 3
4 4 Because of how %run manages namespaces, and the fact that we are trying here to
5 5 verify subtle object deletion and reference counting issues, the %run tests
6 6 will be kept in this separate file. This makes it easier to aggregate in one
7 7 place the tricks needed to handle it; most other magics are much easier to test
8 8 and we do so in a common test_magic file.
9 9
10 10 Note that any test using `run -i` should make sure to do a `reset` afterwards,
11 11 as otherwise it may influence later tests.
12 12 """
13 13
14 14 # Copyright (c) IPython Development Team.
15 15 # Distributed under the terms of the Modified BSD License.
16 16
17 17
18 18
19 19 import functools
20 20 import os
21 21 import platform
22 22 import random
23 23 import string
24 24 import sys
25 25 import textwrap
26 26 import unittest
27 27 from os.path import join as pjoin
28 28 from unittest.mock import patch
29 29
30 30 import pytest
31 31 from tempfile import TemporaryDirectory
32 32
33 33 from IPython.core import debugger
34 34 from IPython.testing import decorators as dec
35 35 from IPython.testing import tools as tt
36 36 from IPython.utils.io import capture_output
37 37
38 38
39 39 def doctest_refbug():
40 40 """Very nasty problem with references held by multiple runs of a script.
41 41 See: https://github.com/ipython/ipython/issues/141
42 42
43 43 In [1]: _ip.clear_main_mod_cache()
44 44 # random
45 45
46 46 In [2]: %run refbug
47 47
48 48 In [3]: call_f()
49 49 lowercased: hello
50 50
51 51 In [4]: %run refbug
52 52
53 53 In [5]: call_f()
54 54 lowercased: hello
55 55 lowercased: hello
56 56 """
57 57
58 58
59 59 def doctest_run_builtins():
60 60 r"""Check that %run doesn't damage __builtins__.
61 61
62 62 In [1]: import tempfile
63 63
64 64 In [2]: bid1 = id(__builtins__)
65 65
66 66 In [3]: fname = tempfile.mkstemp('.py')[1]
67 67
68 68 In [3]: f = open(fname, 'w', encoding='utf-8')
69 69
70 70 In [4]: dummy= f.write('pass\n')
71 71
72 72 In [5]: f.flush()
73 73
74 74 In [6]: t1 = type(__builtins__)
75 75
76 76 In [7]: %run $fname
77 77
78 78 In [7]: f.close()
79 79
80 80 In [8]: bid2 = id(__builtins__)
81 81
82 82 In [9]: t2 = type(__builtins__)
83 83
84 84 In [10]: t1 == t2
85 85 Out[10]: True
86 86
87 87 In [10]: bid1 == bid2
88 88 Out[10]: True
89 89
90 90 In [12]: try:
91 91 ....: os.unlink(fname)
92 92 ....: except:
93 93 ....: pass
94 94 ....:
95 95 """
96 96
97 97
98 98 def doctest_run_option_parser():
99 99 r"""Test option parser in %run.
100 100
101 101 In [1]: %run print_argv.py
102 102 []
103 103
104 104 In [2]: %run print_argv.py print*.py
105 105 ['print_argv.py']
106 106
107 107 In [3]: %run -G print_argv.py print*.py
108 108 ['print*.py']
109 109
110 110 """
111 111
112 112
113 113 @dec.skip_win32
114 114 def doctest_run_option_parser_for_posix():
115 115 r"""Test option parser in %run (Linux/OSX specific).
116 116
117 117 You need double quote to escape glob in POSIX systems:
118 118
119 119 In [1]: %run print_argv.py print\\*.py
120 120 ['print*.py']
121 121
122 122 You can't use quote to escape glob in POSIX systems:
123 123
124 124 In [2]: %run print_argv.py 'print*.py'
125 125 ['print_argv.py']
126 126
127 127 """
128 128
129 129
130 130 doctest_run_option_parser_for_posix.__skip_doctest__ = sys.platform == "win32"
131 131
132 132
133 133 @dec.skip_if_not_win32
134 134 def doctest_run_option_parser_for_windows():
135 135 r"""Test option parser in %run (Windows specific).
136 136
137 137 In Windows, you can't escape ``*` `by backslash:
138 138
139 139 In [1]: %run print_argv.py print\\*.py
140 140 ['print\\\\*.py']
141 141
142 142 You can use quote to escape glob:
143 143
144 144 In [2]: %run print_argv.py 'print*.py'
145 145 ["'print*.py'"]
146 146
147 147 """
148 148
149 149
150 150 doctest_run_option_parser_for_windows.__skip_doctest__ = sys.platform != "win32"
151 151
152 152
153 153 def doctest_reset_del():
154 154 """Test that resetting doesn't cause errors in __del__ methods.
155 155
156 156 In [2]: class A(object):
157 157 ...: def __del__(self):
158 158 ...: print(str("Hi"))
159 159 ...:
160 160
161 161 In [3]: a = A()
162 162
163 163 In [4]: get_ipython().reset(); import gc; x = gc.collect(0)
164 164 Hi
165 165
166 166 In [5]: 1+1
167 167 Out[5]: 2
168 168 """
169 169
170 170 # For some tests, it will be handy to organize them in a class with a common
171 171 # setup that makes a temp file
172 172
173 173 class TestMagicRunPass(tt.TempFileMixin):
174 174
175 175 def setUp(self):
176 176 content = "a = [1,2,3]\nb = 1"
177 177 self.mktmp(content)
178 178
179 179 def run_tmpfile(self):
180 180 _ip = get_ipython()
181 181 # This fails on Windows if self.tmpfile.name has spaces or "~" in it.
182 182 # See below and ticket https://bugs.launchpad.net/bugs/366353
183 183 _ip.run_line_magic("run", self.fname)
184 184
185 185 def run_tmpfile_p(self):
186 186 _ip = get_ipython()
187 187 # This fails on Windows if self.tmpfile.name has spaces or "~" in it.
188 188 # See below and ticket https://bugs.launchpad.net/bugs/366353
189 189 _ip.run_line_magic("run", "-p %s" % self.fname)
190 190
191 191 def test_builtins_id(self):
192 192 """Check that %run doesn't damage __builtins__ """
193 193 _ip = get_ipython()
194 194 # Test that the id of __builtins__ is not modified by %run
195 195 bid1 = id(_ip.user_ns['__builtins__'])
196 196 self.run_tmpfile()
197 197 bid2 = id(_ip.user_ns['__builtins__'])
198 198 assert bid1 == bid2
199 199
200 200 def test_builtins_type(self):
201 201 """Check that the type of __builtins__ doesn't change with %run.
202 202
203 203 However, the above could pass if __builtins__ was already modified to
204 204 be a dict (it should be a module) by a previous use of %run. So we
205 205 also check explicitly that it really is a module:
206 206 """
207 207 _ip = get_ipython()
208 208 self.run_tmpfile()
209 209 assert type(_ip.user_ns["__builtins__"]) == type(sys)
210 210
211 211 def test_run_profile(self):
212 212 """Test that the option -p, which invokes the profiler, do not
213 213 crash by invoking execfile"""
214 214 self.run_tmpfile_p()
215 215
216 216 def test_run_debug_twice(self):
217 217 # https://github.com/ipython/ipython/issues/10028
218 218 _ip = get_ipython()
219 219 with tt.fake_input(["c"]):
220 220 _ip.run_line_magic("run", "-d %s" % self.fname)
221 221 with tt.fake_input(["c"]):
222 222 _ip.run_line_magic("run", "-d %s" % self.fname)
223 223
224 224 def test_run_debug_twice_with_breakpoint(self):
225 225 """Make a valid python temp file."""
226 226 _ip = get_ipython()
227 227 with tt.fake_input(["b 2", "c", "c"]):
228 228 _ip.run_line_magic("run", "-d %s" % self.fname)
229 229
230 230 with tt.fake_input(["c"]):
231 231 with tt.AssertNotPrints("KeyError"):
232 232 _ip.run_line_magic("run", "-d %s" % self.fname)
233 233
234 234
235 235 class TestMagicRunSimple(tt.TempFileMixin):
236 236
237 237 def test_simpledef(self):
238 238 """Test that simple class definitions work."""
239 239 src = ("class foo: pass\n"
240 240 "def f(): return foo()")
241 241 self.mktmp(src)
242 242 _ip.run_line_magic("run", str(self.fname))
243 243 _ip.run_cell("t = isinstance(f(), foo)")
244 244 assert _ip.user_ns["t"] is True
245 245
246 246 @pytest.mark.xfail(
247 247 platform.python_implementation() == "PyPy",
248 248 reason="expecting __del__ call on exit is unreliable and doesn't happen on PyPy",
249 249 )
250 250 def test_obj_del(self):
251 251 """Test that object's __del__ methods are called on exit."""
252 252 src = ("class A(object):\n"
253 253 " def __del__(self):\n"
254 254 " print('object A deleted')\n"
255 255 "a = A()\n")
256 256 self.mktmp(src)
257 257 err = None
258 258 tt.ipexec_validate(self.fname, 'object A deleted', err)
259 259
260 260 def test_aggressive_namespace_cleanup(self):
261 261 """Test that namespace cleanup is not too aggressive GH-238
262 262
263 263 Returning from another run magic deletes the namespace"""
264 264 # see ticket https://github.com/ipython/ipython/issues/238
265 265
266 266 with tt.TempFileMixin() as empty:
267 267 empty.mktmp("")
268 268 # On Windows, the filename will have \users in it, so we need to use the
269 269 # repr so that the \u becomes \\u.
270 270 src = (
271 271 "ip = get_ipython()\n"
272 272 "for i in range(5):\n"
273 273 " try:\n"
274 274 " ip.magic(%r)\n"
275 275 " except NameError as e:\n"
276 276 " print(i)\n"
277 277 " break\n" % ("run " + empty.fname)
278 278 )
279 279 self.mktmp(src)
280 280 _ip.run_line_magic("run", str(self.fname))
281 281 _ip.run_cell("ip == get_ipython()")
282 282 assert _ip.user_ns["i"] == 4
283 283
284 284 def test_run_second(self):
285 285 """Test that running a second file doesn't clobber the first, gh-3547"""
286 286 self.mktmp("avar = 1\n" "def afunc():\n" " return avar\n")
287 287
288 288 with tt.TempFileMixin() as empty:
289 289 empty.mktmp("")
290 290
291 291 _ip.run_line_magic("run", self.fname)
292 292 _ip.run_line_magic("run", empty.fname)
293 293 assert _ip.user_ns["afunc"]() == 1
294 294
295 295 def test_tclass(self):
296 296 mydir = os.path.dirname(__file__)
297 297 tc = os.path.join(mydir, "tclass")
298 298 src = f"""\
299 299 import gc
300 300 %run "{tc}" C-first
301 301 gc.collect(0)
302 302 %run "{tc}" C-second
303 303 gc.collect(0)
304 304 %run "{tc}" C-third
305 305 gc.collect(0)
306 306 %reset -f
307 307 """
308 308 self.mktmp(src, ".ipy")
309 309 out = """\
310 310 ARGV 1-: ['C-first']
311 311 ARGV 1-: ['C-second']
312 312 tclass.py: deleting object: C-first
313 313 ARGV 1-: ['C-third']
314 314 tclass.py: deleting object: C-second
315 315 tclass.py: deleting object: C-third
316 316 """
317 317 err = None
318 318 tt.ipexec_validate(self.fname, out, err)
319 319
320 320 def test_run_i_after_reset(self):
321 321 """Check that %run -i still works after %reset (gh-693)"""
322 322 src = "yy = zz\n"
323 323 self.mktmp(src)
324 324 _ip.run_cell("zz = 23")
325 325 try:
326 326 _ip.run_line_magic("run", "-i %s" % self.fname)
327 327 assert _ip.user_ns["yy"] == 23
328 328 finally:
329 329 _ip.run_line_magic("reset", "-f")
330 330
331 331 _ip.run_cell("zz = 23")
332 332 try:
333 333 _ip.run_line_magic("run", "-i %s" % self.fname)
334 334 assert _ip.user_ns["yy"] == 23
335 335 finally:
336 336 _ip.run_line_magic("reset", "-f")
337 337
338 338 def test_unicode(self):
339 339 """Check that files in odd encodings are accepted."""
340 340 mydir = os.path.dirname(__file__)
341 341 na = os.path.join(mydir, "nonascii.py")
342 342 _ip.magic('run "%s"' % na)
343 343 assert _ip.user_ns["u"] == "ΠŽΡ‚β„–Π€"
344 344
345 345 def test_run_py_file_attribute(self):
346 346 """Test handling of `__file__` attribute in `%run <file>.py`."""
347 347 src = "t = __file__\n"
348 348 self.mktmp(src)
349 349 _missing = object()
350 350 file1 = _ip.user_ns.get("__file__", _missing)
351 351 _ip.run_line_magic("run", self.fname)
352 352 file2 = _ip.user_ns.get("__file__", _missing)
353 353
354 354 # Check that __file__ was equal to the filename in the script's
355 355 # namespace.
356 356 assert _ip.user_ns["t"] == self.fname
357 357
358 358 # Check that __file__ was not leaked back into user_ns.
359 359 assert file1 == file2
360 360
361 361 def test_run_ipy_file_attribute(self):
362 362 """Test handling of `__file__` attribute in `%run <file.ipy>`."""
363 363 src = "t = __file__\n"
364 364 self.mktmp(src, ext='.ipy')
365 365 _missing = object()
366 366 file1 = _ip.user_ns.get("__file__", _missing)
367 367 _ip.run_line_magic("run", self.fname)
368 368 file2 = _ip.user_ns.get("__file__", _missing)
369 369
370 370 # Check that __file__ was equal to the filename in the script's
371 371 # namespace.
372 372 assert _ip.user_ns["t"] == self.fname
373 373
374 374 # Check that __file__ was not leaked back into user_ns.
375 375 assert file1 == file2
376 376
377 377 def test_run_formatting(self):
378 378 """ Test that %run -t -N<N> does not raise a TypeError for N > 1."""
379 379 src = "pass"
380 380 self.mktmp(src)
381 381 _ip.run_line_magic("run", "-t -N 1 %s" % self.fname)
382 382 _ip.run_line_magic("run", "-t -N 10 %s" % self.fname)
383 383
384 384 def test_ignore_sys_exit(self):
385 385 """Test the -e option to ignore sys.exit()"""
386 386 src = "import sys; sys.exit(1)"
387 387 self.mktmp(src)
388 388 with tt.AssertPrints("SystemExit"):
389 389 _ip.run_line_magic("run", self.fname)
390 390
391 391 with tt.AssertNotPrints("SystemExit"):
392 392 _ip.run_line_magic("run", "-e %s" % self.fname)
393 393
394 394 def test_run_nb(self):
395 395 """Test %run notebook.ipynb"""
396 396 pytest.importorskip("nbformat")
397 397 from nbformat import v4, writes
398 398 nb = v4.new_notebook(
399 399 cells=[
400 400 v4.new_markdown_cell("The Ultimate Question of Everything"),
401 401 v4.new_code_cell("answer=42")
402 402 ]
403 403 )
404 404 src = writes(nb, version=4)
405 405 self.mktmp(src, ext='.ipynb')
406 406
407 407 _ip.run_line_magic("run", self.fname)
408 408
409 409 assert _ip.user_ns["answer"] == 42
410 410
411 411 def test_run_nb_error(self):
412 412 """Test %run notebook.ipynb error"""
413 413 pytest.importorskip("nbformat")
414 414 from nbformat import v4, writes
415 415
416 416 # %run when a file name isn't provided
417 417 pytest.raises(Exception, _ip.magic, "run")
418 418
419 419 # %run when a file doesn't exist
420 420 pytest.raises(Exception, _ip.magic, "run foobar.ipynb")
421 421
422 422 # %run on a notebook with an error
423 423 nb = v4.new_notebook(
424 424 cells=[
425 425 v4.new_code_cell("0/0")
426 426 ]
427 427 )
428 428 src = writes(nb, version=4)
429 429 self.mktmp(src, ext='.ipynb')
430 430 pytest.raises(Exception, _ip.magic, "run %s" % self.fname)
431 431
432 432 def test_file_options(self):
433 433 src = ('import sys\n'
434 434 'a = " ".join(sys.argv[1:])\n')
435 435 self.mktmp(src)
436 436 test_opts = "-x 3 --verbose"
437 437 _ip.run_line_magic("run", "{0} {1}".format(self.fname, test_opts))
438 438 assert _ip.user_ns["a"] == test_opts
439 439
440 440
441 441 class TestMagicRunWithPackage(unittest.TestCase):
442 442
443 443 def writefile(self, name, content):
444 444 path = os.path.join(self.tempdir.name, name)
445 445 d = os.path.dirname(path)
446 446 if not os.path.isdir(d):
447 447 os.makedirs(d)
448 448 with open(path, "w", encoding="utf-8") as f:
449 449 f.write(textwrap.dedent(content))
450 450
451 451 def setUp(self):
452 452 self.package = package = 'tmp{0}'.format(''.join([random.choice(string.ascii_letters) for i in range(10)]))
453 453 """Temporary (probably) valid python package name."""
454 454
455 455 self.value = int(random.random() * 10000)
456 456
457 457 self.tempdir = TemporaryDirectory()
458 458 self.__orig_cwd = os.getcwd()
459 459 sys.path.insert(0, self.tempdir.name)
460 460
461 461 self.writefile(os.path.join(package, '__init__.py'), '')
462 462 self.writefile(os.path.join(package, 'sub.py'), """
463 463 x = {0!r}
464 464 """.format(self.value))
465 465 self.writefile(os.path.join(package, 'relative.py'), """
466 466 from .sub import x
467 467 """)
468 468 self.writefile(os.path.join(package, 'absolute.py'), """
469 469 from {0}.sub import x
470 470 """.format(package))
471 471 self.writefile(os.path.join(package, 'args.py'), """
472 472 import sys
473 473 a = " ".join(sys.argv[1:])
474 474 """.format(package))
475 475
476 476 def tearDown(self):
477 477 os.chdir(self.__orig_cwd)
478 478 sys.path[:] = [p for p in sys.path if p != self.tempdir.name]
479 479 self.tempdir.cleanup()
480 480
481 481 def check_run_submodule(self, submodule, opts=""):
482 482 _ip.user_ns.pop("x", None)
483 483 _ip.run_line_magic(
484 484 "run", "{2} -m {0}.{1}".format(self.package, submodule, opts)
485 485 )
486 486 self.assertEqual(
487 487 _ip.user_ns["x"],
488 488 self.value,
489 489 "Variable `x` is not loaded from module `{0}`.".format(submodule),
490 490 )
491 491
492 492 def test_run_submodule_with_absolute_import(self):
493 493 self.check_run_submodule('absolute')
494 494
495 495 def test_run_submodule_with_relative_import(self):
496 496 """Run submodule that has a relative import statement (#2727)."""
497 497 self.check_run_submodule('relative')
498 498
499 499 def test_prun_submodule_with_absolute_import(self):
500 500 self.check_run_submodule('absolute', '-p')
501 501
502 502 def test_prun_submodule_with_relative_import(self):
503 503 self.check_run_submodule('relative', '-p')
504 504
505 505 def with_fake_debugger(func):
506 506 @functools.wraps(func)
507 507 def wrapper(*args, **kwds):
508 508 with patch.object(debugger.Pdb, 'run', staticmethod(eval)):
509 509 return func(*args, **kwds)
510 510 return wrapper
511 511
512 512 @with_fake_debugger
513 513 def test_debug_run_submodule_with_absolute_import(self):
514 514 self.check_run_submodule('absolute', '-d')
515 515
516 516 @with_fake_debugger
517 517 def test_debug_run_submodule_with_relative_import(self):
518 518 self.check_run_submodule('relative', '-d')
519 519
520 520 def test_module_options(self):
521 521 _ip.user_ns.pop("a", None)
522 522 test_opts = "-x abc -m test"
523 523 _ip.run_line_magic("run", "-m {0}.args {1}".format(self.package, test_opts))
524 524 assert _ip.user_ns["a"] == test_opts
525 525
526 526 def test_module_options_with_separator(self):
527 527 _ip.user_ns.pop("a", None)
528 528 test_opts = "-x abc -m test"
529 529 _ip.run_line_magic("run", "-m {0}.args -- {1}".format(self.package, test_opts))
530 530 assert _ip.user_ns["a"] == test_opts
531 531
532 532
533 533 def test_run__name__():
534 534 with TemporaryDirectory() as td:
535 535 path = pjoin(td, "foo.py")
536 536 with open(path, "w", encoding="utf-8") as f:
537 537 f.write("q = __name__")
538 538
539 539 _ip.user_ns.pop("q", None)
540 540 _ip.run_line_magic("run", "{}".format(path))
541 541 assert _ip.user_ns.pop("q") == "__main__"
542 542
543 543 _ip.run_line_magic("run", "-n {}".format(path))
544 544 assert _ip.user_ns.pop("q") == "foo"
545 545
546 546 try:
547 547 _ip.run_line_magic("run", "-i -n {}".format(path))
548 548 assert _ip.user_ns.pop("q") == "foo"
549 549 finally:
550 550 _ip.run_line_magic("reset", "-f")
551 551
552 552
553 553 def test_run_tb():
554 554 """Test traceback offset in %run"""
555 555 with TemporaryDirectory() as td:
556 556 path = pjoin(td, "foo.py")
557 557 with open(path, "w", encoding="utf-8") as f:
558 558 f.write(
559 559 "\n".join(
560 560 [
561 561 "def foo():",
562 562 " return bar()",
563 563 "def bar():",
564 564 " raise RuntimeError('hello!')",
565 565 "foo()",
566 566 ]
567 567 )
568 568 )
569 569 with capture_output() as io:
570 570 _ip.run_line_magic("run", "{}".format(path))
571 571 out = io.stdout
572 572 assert "execfile" not in out
573 573 assert "RuntimeError" in out
574 574 assert out.count("---->") == 3
575 575 del ip.user_ns['bar']
576 576 del ip.user_ns['foo']
577 577
578 578
579 579 def test_multiprocessing_run():
580 580 """Set we can run mutiprocesgin without messing up up main namespace
581 581
582 Note that import `nose.tools as nt` mdify the value s
582 Note that import `nose.tools as nt` modify the values
583 583 sys.module['__mp_main__'] so we need to temporarily set it to None to test
584 584 the issue.
585 585 """
586 586 with TemporaryDirectory() as td:
587 587 mpm = sys.modules.get('__mp_main__')
588 588 sys.modules['__mp_main__'] = None
589 589 try:
590 590 path = pjoin(td, "test.py")
591 591 with open(path, "w", encoding="utf-8") as f:
592 592 f.write("import multiprocessing\nprint('hoy')")
593 593 with capture_output() as io:
594 594 _ip.run_line_magic('run', path)
595 595 _ip.run_cell("i_m_undefined")
596 596 out = io.stdout
597 597 assert "hoy" in out
598 598 assert "AttributeError" not in out
599 599 assert "NameError" in out
600 600 assert out.count("---->") == 1
601 601 except:
602 602 raise
603 603 finally:
604 604 sys.modules['__mp_main__'] = mpm
605 605
606 606
607 607 def test_script_tb():
608 608 """Test traceback offset in `ipython script.py`"""
609 609 with TemporaryDirectory() as td:
610 610 path = pjoin(td, "foo.py")
611 611 with open(path, "w", encoding="utf-8") as f:
612 612 f.write(
613 613 "\n".join(
614 614 [
615 615 "def foo():",
616 616 " return bar()",
617 617 "def bar():",
618 618 " raise RuntimeError('hello!')",
619 619 "foo()",
620 620 ]
621 621 )
622 622 )
623 623 out, err = tt.ipexec(path)
624 624 assert "execfile" not in out
625 625 assert "RuntimeError" in out
626 626 assert out.count("---->") == 3
@@ -1,456 +1,456
1 1 # encoding: utf-8
2 2 """Tests for IPython.core.ultratb
3 3 """
4 4 import io
5 5 import os.path
6 6 import platform
7 7 import re
8 8 import sys
9 9 import traceback
10 10 import unittest
11 11 from textwrap import dedent
12 12
13 13 from tempfile import TemporaryDirectory
14 14
15 15 from IPython.core.ultratb import ColorTB, VerboseTB
16 16 from IPython.testing import tools as tt
17 17 from IPython.testing.decorators import onlyif_unicode_paths, skip_without
18 18 from IPython.utils.syspathcontext import prepended_to_syspath
19 19
20 20 file_1 = """1
21 21 2
22 22 3
23 23 def f():
24 24 1/0
25 25 """
26 26
27 27 file_2 = """def f():
28 28 1/0
29 29 """
30 30
31 31
32 32 def recursionlimit(frames):
33 33 """
34 34 decorator to set the recursion limit temporarily
35 35 """
36 36
37 37 def inner(test_function):
38 38 def wrapper(*args, **kwargs):
39 39 rl = sys.getrecursionlimit()
40 40 sys.setrecursionlimit(frames)
41 41 try:
42 42 return test_function(*args, **kwargs)
43 43 finally:
44 44 sys.setrecursionlimit(rl)
45 45
46 46 return wrapper
47 47
48 48 return inner
49 49
50 50
51 51 class ChangedPyFileTest(unittest.TestCase):
52 52 def test_changing_py_file(self):
53 53 """Traceback produced if the line where the error occurred is missing?
54 54
55 55 https://github.com/ipython/ipython/issues/1456
56 56 """
57 57 with TemporaryDirectory() as td:
58 58 fname = os.path.join(td, "foo.py")
59 59 with open(fname, "w", encoding="utf-8") as f:
60 60 f.write(file_1)
61 61
62 62 with prepended_to_syspath(td):
63 63 ip.run_cell("import foo")
64 64
65 65 with tt.AssertPrints("ZeroDivisionError"):
66 66 ip.run_cell("foo.f()")
67 67
68 68 # Make the file shorter, so the line of the error is missing.
69 69 with open(fname, "w", encoding="utf-8") as f:
70 70 f.write(file_2)
71 71
72 72 # For some reason, this was failing on the *second* call after
73 73 # changing the file, so we call f() twice.
74 74 with tt.AssertNotPrints("Internal Python error", channel='stderr'):
75 75 with tt.AssertPrints("ZeroDivisionError"):
76 76 ip.run_cell("foo.f()")
77 77 with tt.AssertPrints("ZeroDivisionError"):
78 78 ip.run_cell("foo.f()")
79 79
80 80 iso_8859_5_file = u'''# coding: iso-8859-5
81 81
82 82 def fail():
83 83 """Π΄Π±Π˜Π–"""
84 84 1/0 # Π΄Π±Π˜Π–
85 85 '''
86 86
87 87 class NonAsciiTest(unittest.TestCase):
88 88 @onlyif_unicode_paths
89 89 def test_nonascii_path(self):
90 90 # Non-ascii directory name as well.
91 91 with TemporaryDirectory(suffix=u'Γ©') as td:
92 92 fname = os.path.join(td, u"fooΓ©.py")
93 93 with open(fname, "w", encoding="utf-8") as f:
94 94 f.write(file_1)
95 95
96 96 with prepended_to_syspath(td):
97 97 ip.run_cell("import foo")
98 98
99 99 with tt.AssertPrints("ZeroDivisionError"):
100 100 ip.run_cell("foo.f()")
101 101
102 102 def test_iso8859_5(self):
103 103 with TemporaryDirectory() as td:
104 104 fname = os.path.join(td, 'dfghjkl.py')
105 105
106 106 with io.open(fname, 'w', encoding='iso-8859-5') as f:
107 107 f.write(iso_8859_5_file)
108 108
109 109 with prepended_to_syspath(td):
110 110 ip.run_cell("from dfghjkl import fail")
111 111
112 112 with tt.AssertPrints("ZeroDivisionError"):
113 113 with tt.AssertPrints(u'Π΄Π±Π˜Π–', suppress=False):
114 114 ip.run_cell('fail()')
115 115
116 116 def test_nonascii_msg(self):
117 117 cell = u"raise Exception('Γ©')"
118 118 expected = u"Exception('Γ©')"
119 119 ip.run_cell("%xmode plain")
120 120 with tt.AssertPrints(expected):
121 121 ip.run_cell(cell)
122 122
123 123 ip.run_cell("%xmode verbose")
124 124 with tt.AssertPrints(expected):
125 125 ip.run_cell(cell)
126 126
127 127 ip.run_cell("%xmode context")
128 128 with tt.AssertPrints(expected):
129 129 ip.run_cell(cell)
130 130
131 131 ip.run_cell("%xmode minimal")
132 132 with tt.AssertPrints(u"Exception: Γ©"):
133 133 ip.run_cell(cell)
134 134
135 135 # Put this back into Context mode for later tests.
136 136 ip.run_cell("%xmode context")
137 137
138 138 class NestedGenExprTestCase(unittest.TestCase):
139 139 """
140 140 Regression test for the following issues:
141 141 https://github.com/ipython/ipython/issues/8293
142 142 https://github.com/ipython/ipython/issues/8205
143 143 """
144 144 def test_nested_genexpr(self):
145 145 code = dedent(
146 146 """\
147 147 class SpecificException(Exception):
148 148 pass
149 149
150 150 def foo(x):
151 151 raise SpecificException("Success!")
152 152
153 153 sum(sum(foo(x) for _ in [0]) for x in [0])
154 154 """
155 155 )
156 156 with tt.AssertPrints('SpecificException: Success!', suppress=False):
157 157 ip.run_cell(code)
158 158
159 159
160 160 indentationerror_file = """if True:
161 zoon()
161 zoom()
162 162 """
163 163
164 164 class IndentationErrorTest(unittest.TestCase):
165 165 def test_indentationerror_shows_line(self):
166 166 # See issue gh-2398
167 167 with tt.AssertPrints("IndentationError"):
168 with tt.AssertPrints("zoon()", suppress=False):
168 with tt.AssertPrints("zoom()", suppress=False):
169 169 ip.run_cell(indentationerror_file)
170 170
171 171 with TemporaryDirectory() as td:
172 172 fname = os.path.join(td, "foo.py")
173 173 with open(fname, "w", encoding="utf-8") as f:
174 174 f.write(indentationerror_file)
175 175
176 176 with tt.AssertPrints("IndentationError"):
177 with tt.AssertPrints("zoon()", suppress=False):
177 with tt.AssertPrints("zoom()", suppress=False):
178 178 ip.magic('run %s' % fname)
179 179
180 180 @skip_without("pandas")
181 181 def test_dynamic_code():
182 182 code = """
183 183 import pandas
184 184 df = pandas.DataFrame([])
185 185
186 186 # Important: only fails inside of an "exec" call:
187 187 exec("df.foobarbaz()")
188 188 """
189 189
190 190 with tt.AssertPrints("Could not get source"):
191 191 ip.run_cell(code)
192 192
193 193
194 194 se_file_1 = """1
195 195 2
196 196 7/
197 197 """
198 198
199 199 se_file_2 = """7/
200 200 """
201 201
202 202 class SyntaxErrorTest(unittest.TestCase):
203 203
204 204 def test_syntaxerror_no_stacktrace_at_compile_time(self):
205 205 syntax_error_at_compile_time = """
206 206 def foo():
207 207 ..
208 208 """
209 209 with tt.AssertPrints("SyntaxError"):
210 210 ip.run_cell(syntax_error_at_compile_time)
211 211
212 212 with tt.AssertNotPrints("foo()"):
213 213 ip.run_cell(syntax_error_at_compile_time)
214 214
215 215 def test_syntaxerror_stacktrace_when_running_compiled_code(self):
216 216 syntax_error_at_runtime = """
217 217 def foo():
218 218 eval("..")
219 219
220 220 def bar():
221 221 foo()
222 222
223 223 bar()
224 224 """
225 225 with tt.AssertPrints("SyntaxError"):
226 226 ip.run_cell(syntax_error_at_runtime)
227 227 # Assert syntax error during runtime generate stacktrace
228 228 with tt.AssertPrints(["foo()", "bar()"]):
229 229 ip.run_cell(syntax_error_at_runtime)
230 230 del ip.user_ns['bar']
231 231 del ip.user_ns['foo']
232 232
233 233 def test_changing_py_file(self):
234 234 with TemporaryDirectory() as td:
235 235 fname = os.path.join(td, "foo.py")
236 236 with open(fname, "w", encoding="utf-8") as f:
237 237 f.write(se_file_1)
238 238
239 239 with tt.AssertPrints(["7/", "SyntaxError"]):
240 240 ip.magic("run " + fname)
241 241
242 242 # Modify the file
243 243 with open(fname, "w", encoding="utf-8") as f:
244 244 f.write(se_file_2)
245 245
246 246 # The SyntaxError should point to the correct line
247 247 with tt.AssertPrints(["7/", "SyntaxError"]):
248 248 ip.magic("run " + fname)
249 249
250 250 def test_non_syntaxerror(self):
251 251 # SyntaxTB may be called with an error other than a SyntaxError
252 252 # See e.g. gh-4361
253 253 try:
254 254 raise ValueError('QWERTY')
255 255 except ValueError:
256 256 with tt.AssertPrints('QWERTY'):
257 257 ip.showsyntaxerror()
258 258
259 259 import sys
260 260
261 261 if platform.python_implementation() != "PyPy":
262 262 """
263 263 New 3.9 Pgen Parser does not raise Memory error, except on failed malloc.
264 264 """
265 265 class MemoryErrorTest(unittest.TestCase):
266 266 def test_memoryerror(self):
267 267 memoryerror_code = "(" * 200 + ")" * 200
268 268 ip.run_cell(memoryerror_code)
269 269
270 270
271 271 class Python3ChainedExceptionsTest(unittest.TestCase):
272 272 DIRECT_CAUSE_ERROR_CODE = """
273 273 try:
274 274 x = 1 + 2
275 275 print(not_defined_here)
276 276 except Exception as e:
277 277 x += 55
278 278 x - 1
279 279 y = {}
280 280 raise KeyError('uh') from e
281 281 """
282 282
283 283 EXCEPTION_DURING_HANDLING_CODE = """
284 284 try:
285 285 x = 1 + 2
286 286 print(not_defined_here)
287 287 except Exception as e:
288 288 x += 55
289 289 x - 1
290 290 y = {}
291 291 raise KeyError('uh')
292 292 """
293 293
294 294 SUPPRESS_CHAINING_CODE = """
295 295 try:
296 296 1/0
297 297 except Exception:
298 298 raise ValueError("Yikes") from None
299 299 """
300 300
301 301 SYS_EXIT_WITH_CONTEXT_CODE = """
302 302 try:
303 303 1/0
304 304 except Exception as e:
305 305 raise SystemExit(1)
306 306 """
307 307
308 308 def test_direct_cause_error(self):
309 309 with tt.AssertPrints(["KeyError", "NameError", "direct cause"]):
310 310 ip.run_cell(self.DIRECT_CAUSE_ERROR_CODE)
311 311
312 312 def test_exception_during_handling_error(self):
313 313 with tt.AssertPrints(["KeyError", "NameError", "During handling"]):
314 314 ip.run_cell(self.EXCEPTION_DURING_HANDLING_CODE)
315 315
316 316 def test_sysexit_while_handling_error(self):
317 317 with tt.AssertPrints(["SystemExit", "to see the full traceback"]):
318 318 with tt.AssertNotPrints(["another exception"], suppress=False):
319 319 ip.run_cell(self.SYS_EXIT_WITH_CONTEXT_CODE)
320 320
321 321 def test_suppress_exception_chaining(self):
322 322 with tt.AssertNotPrints("ZeroDivisionError"), \
323 323 tt.AssertPrints("ValueError", suppress=False):
324 324 ip.run_cell(self.SUPPRESS_CHAINING_CODE)
325 325
326 326 def test_plain_direct_cause_error(self):
327 327 with tt.AssertPrints(["KeyError", "NameError", "direct cause"]):
328 328 ip.run_cell("%xmode Plain")
329 329 ip.run_cell(self.DIRECT_CAUSE_ERROR_CODE)
330 330 ip.run_cell("%xmode Verbose")
331 331
332 332 def test_plain_exception_during_handling_error(self):
333 333 with tt.AssertPrints(["KeyError", "NameError", "During handling"]):
334 334 ip.run_cell("%xmode Plain")
335 335 ip.run_cell(self.EXCEPTION_DURING_HANDLING_CODE)
336 336 ip.run_cell("%xmode Verbose")
337 337
338 338 def test_plain_suppress_exception_chaining(self):
339 339 with tt.AssertNotPrints("ZeroDivisionError"), \
340 340 tt.AssertPrints("ValueError", suppress=False):
341 341 ip.run_cell("%xmode Plain")
342 342 ip.run_cell(self.SUPPRESS_CHAINING_CODE)
343 343 ip.run_cell("%xmode Verbose")
344 344
345 345
346 346 class RecursionTest(unittest.TestCase):
347 347 DEFINITIONS = """
348 348 def non_recurs():
349 349 1/0
350 350
351 351 def r1():
352 352 r1()
353 353
354 354 def r3a():
355 355 r3b()
356 356
357 357 def r3b():
358 358 r3c()
359 359
360 360 def r3c():
361 361 r3a()
362 362
363 363 def r3o1():
364 364 r3a()
365 365
366 366 def r3o2():
367 367 r3o1()
368 368 """
369 369 def setUp(self):
370 370 ip.run_cell(self.DEFINITIONS)
371 371
372 372 def test_no_recursion(self):
373 373 with tt.AssertNotPrints("skipping similar frames"):
374 374 ip.run_cell("non_recurs()")
375 375
376 376 @recursionlimit(200)
377 377 def test_recursion_one_frame(self):
378 378 with tt.AssertPrints(re.compile(
379 379 r"\[\.\.\. skipping similar frames: r1 at line 5 \(\d{2,3} times\)\]")
380 380 ):
381 381 ip.run_cell("r1()")
382 382
383 383 @recursionlimit(160)
384 384 def test_recursion_three_frames(self):
385 385 with tt.AssertPrints("[... skipping similar frames: "), \
386 386 tt.AssertPrints(re.compile(r"r3a at line 8 \(\d{2} times\)"), suppress=False), \
387 387 tt.AssertPrints(re.compile(r"r3b at line 11 \(\d{2} times\)"), suppress=False), \
388 388 tt.AssertPrints(re.compile(r"r3c at line 14 \(\d{2} times\)"), suppress=False):
389 389 ip.run_cell("r3o2()")
390 390
391 391
392 392 class PEP678NotesReportingTest(unittest.TestCase):
393 393 ERROR_WITH_NOTE = """
394 394 try:
395 395 raise AssertionError("Message")
396 396 except Exception as e:
397 397 try:
398 398 e.add_note("This is a PEP-678 note.")
399 399 except AttributeError: # Python <= 3.10
400 400 e.__notes__ = ("This is a PEP-678 note.",)
401 401 raise
402 402 """
403 403
404 404 def test_verbose_reports_notes(self):
405 405 with tt.AssertPrints(["AssertionError", "Message", "This is a PEP-678 note."]):
406 406 ip.run_cell(self.ERROR_WITH_NOTE)
407 407
408 408 def test_plain_reports_notes(self):
409 409 with tt.AssertPrints(["AssertionError", "Message", "This is a PEP-678 note."]):
410 410 ip.run_cell("%xmode Plain")
411 411 ip.run_cell(self.ERROR_WITH_NOTE)
412 412 ip.run_cell("%xmode Verbose")
413 413
414 414
415 415 #----------------------------------------------------------------------------
416 416
417 417 # module testing (minimal)
418 418 def test_handlers():
419 419 def spam(c, d_e):
420 420 (d, e) = d_e
421 421 x = c + d
422 422 y = c * d
423 423 foo(x, y)
424 424
425 425 def foo(a, b, bar=1):
426 426 eggs(a, b + bar)
427 427
428 428 def eggs(f, g, z=globals()):
429 429 h = f + g
430 430 i = f - g
431 431 return h / i
432 432
433 433 buff = io.StringIO()
434 434
435 435 buff.write('')
436 436 buff.write('*** Before ***')
437 437 try:
438 438 buff.write(spam(1, (2, 3)))
439 439 except:
440 440 traceback.print_exc(file=buff)
441 441
442 442 handler = ColorTB(ostream=buff)
443 443 buff.write('*** ColorTB ***')
444 444 try:
445 445 buff.write(spam(1, (2, 3)))
446 446 except:
447 447 handler(*sys.exc_info())
448 448 buff.write('')
449 449
450 450 handler = VerboseTB(ostream=buff)
451 451 buff.write('*** VerboseTB ***')
452 452 try:
453 453 buff.write(spam(1, (2, 3)))
454 454 except:
455 455 handler(*sys.exc_info())
456 456 buff.write('')
General Comments 0
You need to be logged in to leave comments. Login now