##// END OF EJS Templates
MAINT: mark 3.13.dev failing test as xfail
M Bussonnier -
Show More
@@ -1,574 +1,582 b''
1 1 """Tests for debugging machinery.
2 2 """
3 3
4 4 # Copyright (c) IPython Development Team.
5 5 # Distributed under the terms of the Modified BSD License.
6 6
7 7 import builtins
8 8 import os
9 9 import sys
10 10 import platform
11 11
12 12 from tempfile import NamedTemporaryFile
13 13 from textwrap import dedent
14 14 from unittest.mock import patch
15 15
16 16 from IPython.core import debugger
17 17 from IPython.testing import IPYTHON_TESTING_TIMEOUT_SCALE
18 18 from IPython.testing.decorators import skip_win32
19 19 import pytest
20 20
21 21 #-----------------------------------------------------------------------------
22 22 # Helper classes, from CPython's Pdb test suite
23 23 #-----------------------------------------------------------------------------
24 24
25 25 class _FakeInput(object):
26 26 """
27 27 A fake input stream for pdb's interactive debugger. Whenever a
28 28 line is read, print it (to simulate the user typing it), and then
29 29 return it. The set of lines to return is specified in the
30 30 constructor; they should not have trailing newlines.
31 31 """
32 32 def __init__(self, lines):
33 33 self.lines = iter(lines)
34 34
35 35 def readline(self):
36 36 line = next(self.lines)
37 37 print(line)
38 38 return line+'\n'
39 39
40 40 class PdbTestInput(object):
41 41 """Context manager that makes testing Pdb in doctests easier."""
42 42
43 43 def __init__(self, input):
44 44 self.input = input
45 45
46 46 def __enter__(self):
47 47 self.real_stdin = sys.stdin
48 48 sys.stdin = _FakeInput(self.input)
49 49
50 50 def __exit__(self, *exc):
51 51 sys.stdin = self.real_stdin
52 52
53 53 #-----------------------------------------------------------------------------
54 54 # Tests
55 55 #-----------------------------------------------------------------------------
56 56
57 @pytest.mark.xfail(
58 sys.version_info.releaselevel not in ("final", "candidate"),
59 reason="fails on 3.13.dev",
60 )
57 61 def test_ipdb_magics():
58 62 '''Test calling some IPython magics from ipdb.
59 63
60 64 First, set up some test functions and classes which we can inspect.
61 65
62 66 >>> class ExampleClass(object):
63 67 ... """Docstring for ExampleClass."""
64 68 ... def __init__(self):
65 69 ... """Docstring for ExampleClass.__init__"""
66 70 ... pass
67 71 ... def __str__(self):
68 72 ... return "ExampleClass()"
69 73
70 74 >>> def example_function(x, y, z="hello"):
71 75 ... """Docstring for example_function."""
72 76 ... pass
73 77
74 78 >>> old_trace = sys.gettrace()
75 79
76 80 Create a function which triggers ipdb.
77 81
78 82 >>> def trigger_ipdb():
79 83 ... a = ExampleClass()
80 84 ... debugger.Pdb().set_trace()
81 85
82 86 >>> with PdbTestInput([
83 87 ... 'pdef example_function',
84 88 ... 'pdoc ExampleClass',
85 89 ... 'up',
86 90 ... 'down',
87 91 ... 'list',
88 92 ... 'pinfo a',
89 93 ... 'll',
90 94 ... 'continue',
91 95 ... ]):
92 96 ... trigger_ipdb()
93 97 --Return--
94 98 None
95 99 > <doctest ...>(3)trigger_ipdb()
96 100 1 def trigger_ipdb():
97 101 2 a = ExampleClass()
98 102 ----> 3 debugger.Pdb().set_trace()
99 103 <BLANKLINE>
100 104 ipdb> pdef example_function
101 105 example_function(x, y, z='hello')
102 106 ipdb> pdoc ExampleClass
103 107 Class docstring:
104 108 Docstring for ExampleClass.
105 109 Init docstring:
106 110 Docstring for ExampleClass.__init__
107 111 ipdb> up
108 112 > <doctest ...>(11)<module>()
109 113 7 'pinfo a',
110 114 8 'll',
111 115 9 'continue',
112 116 10 ]):
113 117 ---> 11 trigger_ipdb()
114 118 <BLANKLINE>
115 119 ipdb> down
116 120 None
117 121 > <doctest ...>(3)trigger_ipdb()
118 122 1 def trigger_ipdb():
119 123 2 a = ExampleClass()
120 124 ----> 3 debugger.Pdb().set_trace()
121 125 <BLANKLINE>
122 126 ipdb> list
123 127 1 def trigger_ipdb():
124 128 2 a = ExampleClass()
125 129 ----> 3 debugger.Pdb().set_trace()
126 130 <BLANKLINE>
127 131 ipdb> pinfo a
128 132 Type: ExampleClass
129 133 String form: ExampleClass()
130 134 Namespace: Local...
131 135 Docstring: Docstring for ExampleClass.
132 136 Init docstring: Docstring for ExampleClass.__init__
133 137 ipdb> ll
134 138 1 def trigger_ipdb():
135 139 2 a = ExampleClass()
136 140 ----> 3 debugger.Pdb().set_trace()
137 141 <BLANKLINE>
138 142 ipdb> continue
139 143
140 144 Restore previous trace function, e.g. for coverage.py
141 145
142 146 >>> sys.settrace(old_trace)
143 147 '''
144 148
145 149 def test_ipdb_magics2():
146 150 '''Test ipdb with a very short function.
147 151
148 152 >>> old_trace = sys.gettrace()
149 153
150 154 >>> def bar():
151 155 ... pass
152 156
153 157 Run ipdb.
154 158
155 159 >>> with PdbTestInput([
156 160 ... 'continue',
157 161 ... ]):
158 162 ... debugger.Pdb().runcall(bar)
159 163 > <doctest ...>(2)bar()
160 164 1 def bar():
161 165 ----> 2 pass
162 166 <BLANKLINE>
163 167 ipdb> continue
164 168
165 169 Restore previous trace function, e.g. for coverage.py
166 170
167 171 >>> sys.settrace(old_trace)
168 172 '''
169 173
170 174 def can_quit():
171 175 '''Test that quit work in ipydb
172 176
173 177 >>> old_trace = sys.gettrace()
174 178
175 179 >>> def bar():
176 180 ... pass
177 181
178 182 >>> with PdbTestInput([
179 183 ... 'quit',
180 184 ... ]):
181 185 ... debugger.Pdb().runcall(bar)
182 186 > <doctest ...>(2)bar()
183 187 1 def bar():
184 188 ----> 2 pass
185 189 <BLANKLINE>
186 190 ipdb> quit
187 191
188 192 Restore previous trace function, e.g. for coverage.py
189 193
190 194 >>> sys.settrace(old_trace)
191 195 '''
192 196
193 197
194 198 def can_exit():
195 199 '''Test that quit work in ipydb
196 200
197 201 >>> old_trace = sys.gettrace()
198 202
199 203 >>> def bar():
200 204 ... pass
201 205
202 206 >>> with PdbTestInput([
203 207 ... 'exit',
204 208 ... ]):
205 209 ... debugger.Pdb().runcall(bar)
206 210 > <doctest ...>(2)bar()
207 211 1 def bar():
208 212 ----> 2 pass
209 213 <BLANKLINE>
210 214 ipdb> exit
211 215
212 216 Restore previous trace function, e.g. for coverage.py
213 217
214 218 >>> sys.settrace(old_trace)
215 219 '''
216 220
217 221
218 222 def test_interruptible_core_debugger():
219 223 """The debugger can be interrupted.
220 224
221 225 The presumption is there is some mechanism that causes a KeyboardInterrupt
222 226 (this is implemented in ipykernel). We want to ensure the
223 227 KeyboardInterrupt cause debugging to cease.
224 228 """
225 229 def raising_input(msg="", called=[0]):
226 230 called[0] += 1
227 231 assert called[0] == 1, "input() should only be called once!"
228 232 raise KeyboardInterrupt()
229 233
230 234 tracer_orig = sys.gettrace()
231 235 try:
232 236 with patch.object(builtins, "input", raising_input):
233 237 debugger.InterruptiblePdb().set_trace()
234 238 # The way this test will fail is by set_trace() never exiting,
235 239 # resulting in a timeout by the test runner. The alternative
236 240 # implementation would involve a subprocess, but that adds issues
237 241 # with interrupting subprocesses that are rather complex, so it's
238 242 # simpler just to do it this way.
239 243 finally:
240 244 # restore the original trace function
241 245 sys.settrace(tracer_orig)
242 246
243 247
244 248 @skip_win32
245 249 def test_xmode_skip():
246 250 """that xmode skip frames
247 251
248 252 Not as a doctest as pytest does not run doctests.
249 253 """
250 254 import pexpect
251 255 env = os.environ.copy()
252 256 env["IPY_TEST_SIMPLE_PROMPT"] = "1"
253 257
254 258 child = pexpect.spawn(
255 259 sys.executable, ["-m", "IPython", "--colors=nocolor"], env=env
256 260 )
257 261 child.timeout = 15 * IPYTHON_TESTING_TIMEOUT_SCALE
258 262
259 263 child.expect("IPython")
260 264 child.expect("\n")
261 265 child.expect_exact("In [1]")
262 266
263 267 block = dedent(
264 268 """
265 269 def f():
266 270 __tracebackhide__ = True
267 271 g()
268 272
269 273 def g():
270 274 raise ValueError
271 275
272 276 f()
273 277 """
274 278 )
275 279
276 280 for line in block.splitlines():
277 281 child.sendline(line)
278 282 child.expect_exact(line)
279 283 child.expect_exact("skipping")
280 284
281 285 block = dedent(
282 286 """
283 287 def f():
284 288 __tracebackhide__ = True
285 289 g()
286 290
287 291 def g():
288 292 from IPython.core.debugger import set_trace
289 293 set_trace()
290 294
291 295 f()
292 296 """
293 297 )
294 298
295 299 for line in block.splitlines():
296 300 child.sendline(line)
297 301 child.expect_exact(line)
298 302
299 303 child.expect("ipdb>")
300 304 child.sendline("w")
301 305 child.expect("hidden")
302 306 child.expect("ipdb>")
303 307 child.sendline("skip_hidden false")
304 308 child.sendline("w")
305 309 child.expect("__traceba")
306 310 child.expect("ipdb>")
307 311
308 312 child.close()
309 313
310 314
311 315 skip_decorators_blocks = (
312 316 """
313 317 def helpers_helper():
314 318 pass # should not stop here except breakpoint
315 319 """,
316 320 """
317 321 def helper_1():
318 322 helpers_helper() # should not stop here
319 323 """,
320 324 """
321 325 def helper_2():
322 326 pass # should not stop here
323 327 """,
324 328 """
325 329 def pdb_skipped_decorator2(function):
326 330 def wrapped_fn(*args, **kwargs):
327 331 __debuggerskip__ = True
328 332 helper_2()
329 333 __debuggerskip__ = False
330 334 result = function(*args, **kwargs)
331 335 __debuggerskip__ = True
332 336 helper_2()
333 337 return result
334 338 return wrapped_fn
335 339 """,
336 340 """
337 341 def pdb_skipped_decorator(function):
338 342 def wrapped_fn(*args, **kwargs):
339 343 __debuggerskip__ = True
340 344 helper_1()
341 345 __debuggerskip__ = False
342 346 result = function(*args, **kwargs)
343 347 __debuggerskip__ = True
344 348 helper_2()
345 349 return result
346 350 return wrapped_fn
347 351 """,
348 352 """
349 353 @pdb_skipped_decorator
350 354 @pdb_skipped_decorator2
351 355 def bar(x, y):
352 356 return x * y
353 357 """,
354 358 """import IPython.terminal.debugger as ipdb""",
355 359 """
356 360 def f():
357 361 ipdb.set_trace()
358 362 bar(3, 4)
359 363 """,
360 364 """
361 365 f()
362 366 """,
363 367 )
364 368
365 369
366 370 def _decorator_skip_setup():
367 371 import pexpect
368 372
369 373 env = os.environ.copy()
370 374 env["IPY_TEST_SIMPLE_PROMPT"] = "1"
371 375 env["PROMPT_TOOLKIT_NO_CPR"] = "1"
372 376
373 377 child = pexpect.spawn(
374 378 sys.executable, ["-m", "IPython", "--colors=nocolor"], env=env
375 379 )
376 380 child.timeout = 15 * IPYTHON_TESTING_TIMEOUT_SCALE
377 381
378 382 child.expect("IPython")
379 383 child.expect("\n")
380 384
381 385 child.timeout = 5 * IPYTHON_TESTING_TIMEOUT_SCALE
382 386 child.str_last_chars = 500
383 387
384 388 dedented_blocks = [dedent(b).strip() for b in skip_decorators_blocks]
385 389 in_prompt_number = 1
386 390 for cblock in dedented_blocks:
387 391 child.expect_exact(f"In [{in_prompt_number}]:")
388 392 in_prompt_number += 1
389 393 for line in cblock.splitlines():
390 394 child.sendline(line)
391 395 child.expect_exact(line)
392 396 child.sendline("")
393 397 return child
394 398
395 399
396 400 @pytest.mark.skip(reason="recently fail for unknown reason on CI")
397 401 @skip_win32
398 402 def test_decorator_skip():
399 403 """test that decorator frames can be skipped."""
400 404
401 405 child = _decorator_skip_setup()
402 406
403 407 child.expect_exact("ipython-input-8")
404 408 child.expect_exact("3 bar(3, 4)")
405 409 child.expect("ipdb>")
406 410
407 411 child.expect("ipdb>")
408 412 child.sendline("step")
409 413 child.expect_exact("step")
410 414 child.expect_exact("--Call--")
411 415 child.expect_exact("ipython-input-6")
412 416
413 417 child.expect_exact("1 @pdb_skipped_decorator")
414 418
415 419 child.sendline("s")
416 420 child.expect_exact("return x * y")
417 421
418 422 child.close()
419 423
420 424
421 425 @pytest.mark.skip(reason="recently fail for unknown reason on CI")
422 426 @pytest.mark.skipif(platform.python_implementation() == "PyPy", reason="issues on PyPy")
423 427 @skip_win32
424 428 def test_decorator_skip_disabled():
425 429 """test that decorator frame skipping can be disabled"""
426 430
427 431 child = _decorator_skip_setup()
428 432
429 433 child.expect_exact("3 bar(3, 4)")
430 434
431 435 for input_, expected in [
432 436 ("skip_predicates debuggerskip False", ""),
433 437 ("skip_predicates", "debuggerskip : False"),
434 438 ("step", "---> 2 def wrapped_fn"),
435 439 ("step", "----> 3 __debuggerskip__"),
436 440 ("step", "----> 4 helper_1()"),
437 441 ("step", "---> 1 def helper_1():"),
438 442 ("next", "----> 2 helpers_helper()"),
439 443 ("next", "--Return--"),
440 444 ("next", "----> 5 __debuggerskip__ = False"),
441 445 ]:
442 446 child.expect("ipdb>")
443 447 child.sendline(input_)
444 448 child.expect_exact(input_)
445 449 child.expect_exact(expected)
446 450
447 451 child.close()
448 452
449 453
454 @pytest.mark.xfail(
455 sys.version_info.releaselevel not in ("final", "candidate"),
456 reason="fails on 3.13.dev",
457 )
450 458 @pytest.mark.skipif(platform.python_implementation() == "PyPy", reason="issues on PyPy")
451 459 @skip_win32
452 460 def test_decorator_skip_with_breakpoint():
453 461 """test that decorator frame skipping can be disabled"""
454 462
455 463 import pexpect
456 464
457 465 env = os.environ.copy()
458 466 env["IPY_TEST_SIMPLE_PROMPT"] = "1"
459 467 env["PROMPT_TOOLKIT_NO_CPR"] = "1"
460 468
461 469 child = pexpect.spawn(
462 470 sys.executable, ["-m", "IPython", "--colors=nocolor"], env=env
463 471 )
464 472 child.timeout = 15 * IPYTHON_TESTING_TIMEOUT_SCALE
465 473 child.str_last_chars = 500
466 474
467 475 child.expect("IPython")
468 476 child.expect("\n")
469 477
470 478 child.timeout = 5 * IPYTHON_TESTING_TIMEOUT_SCALE
471 479
472 480 ### we need a filename, so we need to exec the full block with a filename
473 481 with NamedTemporaryFile(suffix=".py", dir=".", delete=True) as tf:
474 482 name = tf.name[:-3].split("/")[-1]
475 483 tf.write("\n".join([dedent(x) for x in skip_decorators_blocks[:-1]]).encode())
476 484 tf.flush()
477 485 codeblock = f"from {name} import f"
478 486
479 487 dedented_blocks = [
480 488 codeblock,
481 489 "f()",
482 490 ]
483 491
484 492 in_prompt_number = 1
485 493 for cblock in dedented_blocks:
486 494 child.expect_exact(f"In [{in_prompt_number}]:")
487 495 in_prompt_number += 1
488 496 for line in cblock.splitlines():
489 497 child.sendline(line)
490 498 child.expect_exact(line)
491 499 child.sendline("")
492 500
493 501 # as the filename does not exists, we'll rely on the filename prompt
494 502 child.expect_exact("47 bar(3, 4)")
495 503
496 504 for input_, expected in [
497 505 (f"b {name}.py:3", ""),
498 506 ("step", "1---> 3 pass # should not stop here except"),
499 507 ("step", "---> 38 @pdb_skipped_decorator"),
500 508 ("continue", ""),
501 509 ]:
502 510 child.expect("ipdb>")
503 511 child.sendline(input_)
504 512 child.expect_exact(input_)
505 513 child.expect_exact(expected)
506 514
507 515 child.close()
508 516
509 517
510 518 @skip_win32
511 519 def test_where_erase_value():
512 520 """Test that `where` does not access f_locals and erase values."""
513 521 import pexpect
514 522
515 523 env = os.environ.copy()
516 524 env["IPY_TEST_SIMPLE_PROMPT"] = "1"
517 525
518 526 child = pexpect.spawn(
519 527 sys.executable, ["-m", "IPython", "--colors=nocolor"], env=env
520 528 )
521 529 child.timeout = 15 * IPYTHON_TESTING_TIMEOUT_SCALE
522 530
523 531 child.expect("IPython")
524 532 child.expect("\n")
525 533 child.expect_exact("In [1]")
526 534
527 535 block = dedent(
528 536 """
529 537 def simple_f():
530 538 myvar = 1
531 539 print(myvar)
532 540 1/0
533 541 print(myvar)
534 542 simple_f() """
535 543 )
536 544
537 545 for line in block.splitlines():
538 546 child.sendline(line)
539 547 child.expect_exact(line)
540 548 child.expect_exact("ZeroDivisionError")
541 549 child.expect_exact("In [2]:")
542 550
543 551 child.sendline("%debug")
544 552
545 553 ##
546 554 child.expect("ipdb>")
547 555
548 556 child.sendline("myvar")
549 557 child.expect("1")
550 558
551 559 ##
552 560 child.expect("ipdb>")
553 561
554 562 child.sendline("myvar = 2")
555 563
556 564 ##
557 565 child.expect_exact("ipdb>")
558 566
559 567 child.sendline("myvar")
560 568
561 569 child.expect_exact("2")
562 570
563 571 ##
564 572 child.expect("ipdb>")
565 573 child.sendline("where")
566 574
567 575 ##
568 576 child.expect("ipdb>")
569 577 child.sendline("myvar")
570 578
571 579 child.expect_exact("2")
572 580 child.expect("ipdb>")
573 581
574 582 child.close()
@@ -1,579 +1,583 b''
1 1 """Tests for the object inspection functionality.
2 2 """
3 3
4 4 # Copyright (c) IPython Development Team.
5 5 # Distributed under the terms of the Modified BSD License.
6 6
7 7
8 8 from contextlib import contextmanager
9 9 from inspect import signature, Signature, Parameter
10 10 import inspect
11 11 import os
12 12 import pytest
13 13 import re
14 14 import sys
15 15
16 16 from .. import oinspect
17 17
18 18 from decorator import decorator
19 19
20 20 from IPython.testing.tools import AssertPrints, AssertNotPrints
21 21 from IPython.utils.path import compress_user
22 22
23 23
24 24 #-----------------------------------------------------------------------------
25 25 # Globals and constants
26 26 #-----------------------------------------------------------------------------
27 27
28 28 inspector = None
29 29
30 30 def setup_module():
31 31 global inspector
32 32 inspector = oinspect.Inspector()
33 33
34 34
35 35 class SourceModuleMainTest:
36 36 __module__ = "__main__"
37 37
38 38
39 39 #-----------------------------------------------------------------------------
40 40 # Local utilities
41 41 #-----------------------------------------------------------------------------
42 42
43 43 # WARNING: since this test checks the line number where a function is
44 44 # defined, if any code is inserted above, the following line will need to be
45 45 # updated. Do NOT insert any whitespace between the next line and the function
46 46 # definition below.
47 47 THIS_LINE_NUMBER = 47 # Put here the actual number of this line
48 48
49 49
50 50 def test_find_source_lines():
51 51 assert oinspect.find_source_lines(test_find_source_lines) == THIS_LINE_NUMBER + 3
52 52 assert oinspect.find_source_lines(type) is None
53 53 assert oinspect.find_source_lines(SourceModuleMainTest) is None
54 54 assert oinspect.find_source_lines(SourceModuleMainTest()) is None
55 55
56 56
57 57 def test_getsource():
58 58 assert oinspect.getsource(type) is None
59 59 assert oinspect.getsource(SourceModuleMainTest) is None
60 60 assert oinspect.getsource(SourceModuleMainTest()) is None
61 61
62 62
63 63 def test_inspect_getfile_raises_exception():
64 64 """Check oinspect.find_file/getsource/find_source_lines expectations"""
65 65 with pytest.raises(TypeError):
66 66 inspect.getfile(type)
67 67 with pytest.raises(OSError):
68 68 inspect.getfile(SourceModuleMainTest)
69 69
70 70
71 71 # A couple of utilities to ensure these tests work the same from a source or a
72 72 # binary install
73 73 def pyfile(fname):
74 74 return os.path.normcase(re.sub('.py[co]$', '.py', fname))
75 75
76 76
77 77 def match_pyfiles(f1, f2):
78 78 assert pyfile(f1) == pyfile(f2)
79 79
80 80
81 81 def test_find_file():
82 82 match_pyfiles(oinspect.find_file(test_find_file), os.path.abspath(__file__))
83 83 assert oinspect.find_file(type) is None
84 84 assert oinspect.find_file(SourceModuleMainTest) is None
85 85 assert oinspect.find_file(SourceModuleMainTest()) is None
86 86
87 87
88 88 def test_find_file_decorated1():
89 89
90 90 @decorator
91 91 def noop1(f):
92 92 def wrapper(*a, **kw):
93 93 return f(*a, **kw)
94 94 return wrapper
95 95
96 96 @noop1
97 97 def f(x):
98 98 "My docstring"
99 99
100 100 match_pyfiles(oinspect.find_file(f), os.path.abspath(__file__))
101 101 assert f.__doc__ == "My docstring"
102 102
103 103
104 104 def test_find_file_decorated2():
105 105
106 106 @decorator
107 107 def noop2(f, *a, **kw):
108 108 return f(*a, **kw)
109 109
110 110 @noop2
111 111 @noop2
112 112 @noop2
113 113 def f(x):
114 114 "My docstring 2"
115 115
116 116 match_pyfiles(oinspect.find_file(f), os.path.abspath(__file__))
117 117 assert f.__doc__ == "My docstring 2"
118 118
119 119
120 120 def test_find_file_magic():
121 121 run = ip.find_line_magic('run')
122 122 assert oinspect.find_file(run) is not None
123 123
124 124
125 125 # A few generic objects we can then inspect in the tests below
126 126
127 127 class Call(object):
128 128 """This is the class docstring."""
129 129
130 130 def __init__(self, x, y=1):
131 131 """This is the constructor docstring."""
132 132
133 133 def __call__(self, *a, **kw):
134 134 """This is the call docstring."""
135 135
136 136 def method(self, x, z=2):
137 137 """Some method's docstring"""
138 138
139 139 class HasSignature(object):
140 140 """This is the class docstring."""
141 141 __signature__ = Signature([Parameter('test', Parameter.POSITIONAL_OR_KEYWORD)])
142 142
143 143 def __init__(self, *args):
144 144 """This is the init docstring"""
145 145
146 146
147 147 class SimpleClass(object):
148 148 def method(self, x, z=2):
149 149 """Some method's docstring"""
150 150
151 151
152 152 class Awkward(object):
153 153 def __getattr__(self, name):
154 154 raise Exception(name)
155 155
156 156 class NoBoolCall:
157 157 """
158 158 callable with `__bool__` raising should still be inspect-able.
159 159 """
160 160
161 161 def __call__(self):
162 162 """does nothing"""
163 163 pass
164 164
165 165 def __bool__(self):
166 166 """just raise NotImplemented"""
167 167 raise NotImplementedError('Must be implemented')
168 168
169 169
170 170 class SerialLiar(object):
171 171 """Attribute accesses always get another copy of the same class.
172 172
173 173 unittest.mock.call does something similar, but it's not ideal for testing
174 174 as the failure mode is to eat all your RAM. This gives up after 10k levels.
175 175 """
176 176 def __init__(self, max_fibbing_twig, lies_told=0):
177 177 if lies_told > 10000:
178 178 raise RuntimeError('Nose too long, honesty is the best policy')
179 179 self.max_fibbing_twig = max_fibbing_twig
180 180 self.lies_told = lies_told
181 181 max_fibbing_twig[0] = max(max_fibbing_twig[0], lies_told)
182 182
183 183 def __getattr__(self, item):
184 184 return SerialLiar(self.max_fibbing_twig, self.lies_told + 1)
185 185
186 186 #-----------------------------------------------------------------------------
187 187 # Tests
188 188 #-----------------------------------------------------------------------------
189 189
190 190 def test_info():
191 191 "Check that Inspector.info fills out various fields as expected."
192 192 i = inspector.info(Call, oname="Call")
193 193 assert i["type_name"] == "type"
194 194 expected_class = str(type(type)) # <class 'type'> (Python 3) or <type 'type'>
195 195 assert i["base_class"] == expected_class
196 196 assert re.search(
197 197 "<class 'IPython.core.tests.test_oinspect.Call'( at 0x[0-9a-f]{1,9})?>",
198 198 i["string_form"],
199 199 )
200 200 fname = __file__
201 201 if fname.endswith(".pyc"):
202 202 fname = fname[:-1]
203 203 # case-insensitive comparison needed on some filesystems
204 204 # e.g. Windows:
205 205 assert i["file"].lower() == compress_user(fname).lower()
206 206 assert i["definition"] == None
207 207 assert i["docstring"] == Call.__doc__
208 208 assert i["source"] == None
209 209 assert i["isclass"] is True
210 210 assert i["init_definition"] == "Call(x, y=1)"
211 211 assert i["init_docstring"] == Call.__init__.__doc__
212 212
213 213 i = inspector.info(Call, detail_level=1)
214 214 assert i["source"] is not None
215 215 assert i["docstring"] == None
216 216
217 217 c = Call(1)
218 218 c.__doc__ = "Modified instance docstring"
219 219 i = inspector.info(c)
220 220 assert i["type_name"] == "Call"
221 221 assert i["docstring"] == "Modified instance docstring"
222 222 assert i["class_docstring"] == Call.__doc__
223 223 assert i["init_docstring"] == Call.__init__.__doc__
224 224 assert i["call_docstring"] == Call.__call__.__doc__
225 225
226 226
227 227 def test_class_signature():
228 228 info = inspector.info(HasSignature, "HasSignature")
229 229 assert info["init_definition"] == "HasSignature(test)"
230 230 assert info["init_docstring"] == HasSignature.__init__.__doc__
231 231
232 232
233 233 def test_info_awkward():
234 234 # Just test that this doesn't throw an error.
235 235 inspector.info(Awkward())
236 236
237 237 def test_bool_raise():
238 238 inspector.info(NoBoolCall())
239 239
240 240 def test_info_serialliar():
241 241 fib_tracker = [0]
242 242 inspector.info(SerialLiar(fib_tracker))
243 243
244 244 # Nested attribute access should be cut off at 100 levels deep to avoid
245 245 # infinite loops: https://github.com/ipython/ipython/issues/9122
246 246 assert fib_tracker[0] < 9000
247 247
248 248 def support_function_one(x, y=2, *a, **kw):
249 249 """A simple function."""
250 250
251 251 def test_calldef_none():
252 252 # We should ignore __call__ for all of these.
253 253 for obj in [support_function_one, SimpleClass().method, any, str.upper]:
254 254 i = inspector.info(obj)
255 255 assert i["call_def"] is None
256 256
257 257
258 258 def f_kwarg(pos, *, kwonly):
259 259 pass
260 260
261 261 def test_definition_kwonlyargs():
262 262 i = inspector.info(f_kwarg, oname="f_kwarg") # analysis:ignore
263 263 assert i["definition"] == "f_kwarg(pos, *, kwonly)"
264 264
265 265
266 266 def test_getdoc():
267 267 class A(object):
268 268 """standard docstring"""
269 269 pass
270 270
271 271 class B(object):
272 272 """standard docstring"""
273 273 def getdoc(self):
274 274 return "custom docstring"
275 275
276 276 class C(object):
277 277 """standard docstring"""
278 278 def getdoc(self):
279 279 return None
280 280
281 281 a = A()
282 282 b = B()
283 283 c = C()
284 284
285 285 assert oinspect.getdoc(a) == "standard docstring"
286 286 assert oinspect.getdoc(b) == "custom docstring"
287 287 assert oinspect.getdoc(c) == "standard docstring"
288 288
289 289
290 290 def test_empty_property_has_no_source():
291 291 i = inspector.info(property(), detail_level=1)
292 292 assert i["source"] is None
293 293
294 294
295 295 def test_property_sources():
296 296 # A simple adder whose source and signature stays
297 297 # the same across Python distributions
298 298 def simple_add(a, b):
299 299 "Adds two numbers"
300 300 return a + b
301 301
302 302 class A(object):
303 303 @property
304 304 def foo(self):
305 305 return 'bar'
306 306
307 307 foo = foo.setter(lambda self, v: setattr(self, 'bar', v))
308 308
309 309 dname = property(oinspect.getdoc)
310 310 adder = property(simple_add)
311 311
312 312 i = inspector.info(A.foo, detail_level=1)
313 313 assert "def foo(self):" in i["source"]
314 314 assert "lambda self, v:" in i["source"]
315 315
316 316 i = inspector.info(A.dname, detail_level=1)
317 317 assert "def getdoc(obj)" in i["source"]
318 318
319 319 i = inspector.info(A.adder, detail_level=1)
320 320 assert "def simple_add(a, b)" in i["source"]
321 321
322 322
323 323 def test_property_docstring_is_in_info_for_detail_level_0():
324 324 class A(object):
325 325 @property
326 326 def foobar(self):
327 327 """This is `foobar` property."""
328 328 pass
329 329
330 330 ip.user_ns["a_obj"] = A()
331 331 assert (
332 332 "This is `foobar` property."
333 333 == ip.object_inspect("a_obj.foobar", detail_level=0)["docstring"]
334 334 )
335 335
336 336 ip.user_ns["a_cls"] = A
337 337 assert (
338 338 "This is `foobar` property."
339 339 == ip.object_inspect("a_cls.foobar", detail_level=0)["docstring"]
340 340 )
341 341
342 342
343 343 def test_pdef():
344 344 # See gh-1914
345 345 def foo(): pass
346 346 inspector.pdef(foo, 'foo')
347 347
348 348
349 349 @contextmanager
350 350 def cleanup_user_ns(**kwargs):
351 351 """
352 352 On exit delete all the keys that were not in user_ns before entering.
353 353
354 354 It does not restore old values !
355 355
356 356 Parameters
357 357 ----------
358 358
359 359 **kwargs
360 360 used to update ip.user_ns
361 361
362 362 """
363 363 try:
364 364 known = set(ip.user_ns.keys())
365 365 ip.user_ns.update(kwargs)
366 366 yield
367 367 finally:
368 368 added = set(ip.user_ns.keys()) - known
369 369 for k in added:
370 370 del ip.user_ns[k]
371 371
372 372
373 373 def test_pinfo_bool_raise():
374 374 """
375 375 Test that bool method is not called on parent.
376 376 """
377 377
378 378 class RaiseBool:
379 379 attr = None
380 380
381 381 def __bool__(self):
382 382 raise ValueError("pinfo should not access this method")
383 383
384 384 raise_bool = RaiseBool()
385 385
386 386 with cleanup_user_ns(raise_bool=raise_bool):
387 387 ip._inspect("pinfo", "raise_bool.attr", detail_level=0)
388 388
389 389
390 390 def test_pinfo_getindex():
391 391 def dummy():
392 392 """
393 393 MARKER
394 394 """
395 395
396 396 container = [dummy]
397 397 with cleanup_user_ns(container=container):
398 398 with AssertPrints("MARKER"):
399 399 ip._inspect("pinfo", "container[0]", detail_level=0)
400 400 assert "container" not in ip.user_ns.keys()
401 401
402 402
403 403 def test_qmark_getindex():
404 404 def dummy():
405 405 """
406 406 MARKER 2
407 407 """
408 408
409 409 container = [dummy]
410 410 with cleanup_user_ns(container=container):
411 411 with AssertPrints("MARKER 2"):
412 412 ip.run_cell("container[0]?")
413 413 assert "container" not in ip.user_ns.keys()
414 414
415 415
416 416 def test_qmark_getindex_negatif():
417 417 def dummy():
418 418 """
419 419 MARKER 3
420 420 """
421 421
422 422 container = [dummy]
423 423 with cleanup_user_ns(container=container):
424 424 with AssertPrints("MARKER 3"):
425 425 ip.run_cell("container[-1]?")
426 426 assert "container" not in ip.user_ns.keys()
427 427
428 428
429 429
430 430 def test_pinfo_nonascii():
431 431 # See gh-1177
432 432 from . import nonascii2
433 433 ip.user_ns['nonascii2'] = nonascii2
434 434 ip._inspect('pinfo', 'nonascii2', detail_level=1)
435 435
436 436 def test_pinfo_type():
437 437 """
438 438 type can fail in various edge case, for example `type.__subclass__()`
439 439 """
440 440 ip._inspect('pinfo', 'type')
441 441
442 442
443 443 def test_pinfo_docstring_no_source():
444 444 """Docstring should be included with detail_level=1 if there is no source"""
445 445 with AssertPrints('Docstring:'):
446 446 ip._inspect('pinfo', 'str.format', detail_level=0)
447 447 with AssertPrints('Docstring:'):
448 448 ip._inspect('pinfo', 'str.format', detail_level=1)
449 449
450 450
451 451 def test_pinfo_no_docstring_if_source():
452 452 """Docstring should not be included with detail_level=1 if source is found"""
453 453 def foo():
454 454 """foo has a docstring"""
455 455
456 456 ip.user_ns['foo'] = foo
457 457
458 458 with AssertPrints('Docstring:'):
459 459 ip._inspect('pinfo', 'foo', detail_level=0)
460 460 with AssertPrints('Source:'):
461 461 ip._inspect('pinfo', 'foo', detail_level=1)
462 462 with AssertNotPrints('Docstring:'):
463 463 ip._inspect('pinfo', 'foo', detail_level=1)
464 464
465 465
466 466 def test_pinfo_docstring_if_detail_and_no_source():
467 467 """ Docstring should be displayed if source info not available """
468 468 obj_def = '''class Foo(object):
469 469 """ This is a docstring for Foo """
470 470 def bar(self):
471 471 """ This is a docstring for Foo.bar """
472 472 pass
473 473 '''
474 474
475 475 ip.run_cell(obj_def)
476 476 ip.run_cell('foo = Foo()')
477 477
478 478 with AssertNotPrints("Source:"):
479 479 with AssertPrints('Docstring:'):
480 480 ip._inspect('pinfo', 'foo', detail_level=0)
481 481 with AssertPrints('Docstring:'):
482 482 ip._inspect('pinfo', 'foo', detail_level=1)
483 483 with AssertPrints('Docstring:'):
484 484 ip._inspect('pinfo', 'foo.bar', detail_level=0)
485 485
486 486 with AssertNotPrints('Docstring:'):
487 487 with AssertPrints('Source:'):
488 488 ip._inspect('pinfo', 'foo.bar', detail_level=1)
489 489
490 490
491 @pytest.mark.xfail(
492 sys.version_info.releaselevel not in ("final", "candidate"),
493 reason="fails on 3.13.dev",
494 )
491 495 def test_pinfo_docstring_dynamic():
492 496 obj_def = """class Bar:
493 497 __custom_documentations__ = {
494 498 "prop" : "cdoc for prop",
495 499 "non_exist" : "cdoc for non_exist",
496 500 }
497 501 @property
498 502 def prop(self):
499 503 '''
500 504 Docstring for prop
501 505 '''
502 506 return self._prop
503 507
504 508 @prop.setter
505 509 def prop(self, v):
506 510 self._prop = v
507 511 """
508 512 ip.run_cell(obj_def)
509 513
510 514 ip.run_cell("b = Bar()")
511 515
512 516 with AssertPrints("Docstring: cdoc for prop"):
513 517 ip.run_line_magic("pinfo", "b.prop")
514 518
515 519 with AssertPrints("Docstring: cdoc for non_exist"):
516 520 ip.run_line_magic("pinfo", "b.non_exist")
517 521
518 522 with AssertPrints("Docstring: cdoc for prop"):
519 523 ip.run_cell("b.prop?")
520 524
521 525 with AssertPrints("Docstring: cdoc for non_exist"):
522 526 ip.run_cell("b.non_exist?")
523 527
524 528 with AssertPrints("Docstring: <no docstring>"):
525 529 ip.run_cell("b.undefined?")
526 530
527 531
528 532 def test_pinfo_magic():
529 533 with AssertPrints("Docstring:"):
530 534 ip._inspect("pinfo", "lsmagic", detail_level=0)
531 535
532 536 with AssertPrints("Source:"):
533 537 ip._inspect("pinfo", "lsmagic", detail_level=1)
534 538
535 539
536 540 def test_init_colors():
537 541 # ensure colors are not present in signature info
538 542 info = inspector.info(HasSignature)
539 543 init_def = info["init_definition"]
540 544 assert "[0m" not in init_def
541 545
542 546
543 547 def test_builtin_init():
544 548 info = inspector.info(list)
545 549 init_def = info['init_definition']
546 550 assert init_def is not None
547 551
548 552
549 553 def test_render_signature_short():
550 554 def short_fun(a=1): pass
551 555 sig = oinspect._render_signature(
552 556 signature(short_fun),
553 557 short_fun.__name__,
554 558 )
555 559 assert sig == "short_fun(a=1)"
556 560
557 561
558 562 def test_render_signature_long():
559 563 from typing import Optional
560 564
561 565 def long_function(
562 566 a_really_long_parameter: int,
563 567 and_another_long_one: bool = False,
564 568 let_us_make_sure_this_is_looong: Optional[str] = None,
565 569 ) -> bool: pass
566 570
567 571 sig = oinspect._render_signature(
568 572 signature(long_function),
569 573 long_function.__name__,
570 574 )
571 575 expected = """\
572 576 long_function(
573 577 a_really_long_parameter: int,
574 578 and_another_long_one: bool = False,
575 579 let_us_make_sure_this_is_looong: Optional[str] = None,
576 580 ) -> bool\
577 581 """
578 582
579 583 assert sig == expected
General Comments 0
You need to be logged in to leave comments. Login now