##// END OF EJS Templates
Life, the Universe, and Everything
Paul Ivanov -
Show More
@@ -1,454 +1,454 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 import os
9 9 import re
10 10 import sys
11 11
12 12 import nose.tools as nt
13 13
14 14 from .. import oinspect
15 15 from IPython.core.magic import (Magics, magics_class, line_magic,
16 16 cell_magic, line_cell_magic,
17 17 register_line_magic, register_cell_magic,
18 18 register_line_cell_magic)
19 19 from decorator import decorator
20 20 from IPython.testing.decorators import skipif
21 21 from IPython.testing.tools import AssertPrints
22 22 from IPython.utils.path import compress_user
23 23 from IPython.utils import py3compat
24 24 from IPython.utils.signatures import Signature, Parameter
25 25
26 26
27 27 #-----------------------------------------------------------------------------
28 28 # Globals and constants
29 29 #-----------------------------------------------------------------------------
30 30
31 31 inspector = oinspect.Inspector()
32 32 ip = get_ipython()
33 33
34 34 #-----------------------------------------------------------------------------
35 35 # Local utilities
36 36 #-----------------------------------------------------------------------------
37 37
38 38 # WARNING: since this test checks the line number where a function is
39 39 # defined, if any code is inserted above, the following line will need to be
40 40 # updated. Do NOT insert any whitespace between the next line and the function
41 41 # definition below.
42 THIS_LINE_NUMBER = 43 # Put here the actual number of this line
42 THIS_LINE_NUMBER = 42 # Put here the actual number of this line
43 43
44 44 from unittest import TestCase
45 45
46 46 class Test(TestCase):
47 47
48 48 def test_find_source_lines(self):
49 49 self.assertEqual(oinspect.find_source_lines(Test.test_find_source_lines),
50 50 THIS_LINE_NUMBER+6)
51 51
52 52
53 53 # A couple of utilities to ensure these tests work the same from a source or a
54 54 # binary install
55 55 def pyfile(fname):
56 56 return os.path.normcase(re.sub('.py[co]$', '.py', fname))
57 57
58 58
59 59 def match_pyfiles(f1, f2):
60 60 nt.assert_equal(pyfile(f1), pyfile(f2))
61 61
62 62
63 63 def test_find_file():
64 64 match_pyfiles(oinspect.find_file(test_find_file), os.path.abspath(__file__))
65 65
66 66
67 67 def test_find_file_decorated1():
68 68
69 69 @decorator
70 70 def noop1(f):
71 71 def wrapper():
72 72 return f(*a, **kw)
73 73 return wrapper
74 74
75 75 @noop1
76 76 def f(x):
77 77 "My docstring"
78 78
79 79 match_pyfiles(oinspect.find_file(f), os.path.abspath(__file__))
80 80 nt.assert_equal(f.__doc__, "My docstring")
81 81
82 82
83 83 def test_find_file_decorated2():
84 84
85 85 @decorator
86 86 def noop2(f, *a, **kw):
87 87 return f(*a, **kw)
88 88
89 89 @noop2
90 90 @noop2
91 91 @noop2
92 92 def f(x):
93 93 "My docstring 2"
94 94
95 95 match_pyfiles(oinspect.find_file(f), os.path.abspath(__file__))
96 96 nt.assert_equal(f.__doc__, "My docstring 2")
97 97
98 98
99 99 def test_find_file_magic():
100 100 run = ip.find_line_magic('run')
101 101 nt.assert_not_equal(oinspect.find_file(run), None)
102 102
103 103
104 104 # A few generic objects we can then inspect in the tests below
105 105
106 106 class Call(object):
107 107 """This is the class docstring."""
108 108
109 109 def __init__(self, x, y=1):
110 110 """This is the constructor docstring."""
111 111
112 112 def __call__(self, *a, **kw):
113 113 """This is the call docstring."""
114 114
115 115 def method(self, x, z=2):
116 116 """Some method's docstring"""
117 117
118 118 class HasSignature(object):
119 119 """This is the class docstring."""
120 120 __signature__ = Signature([Parameter('test', Parameter.POSITIONAL_OR_KEYWORD)])
121 121
122 122 def __init__(self, *args):
123 123 """This is the init docstring"""
124 124
125 125
126 126 class SimpleClass(object):
127 127 def method(self, x, z=2):
128 128 """Some method's docstring"""
129 129
130 130
131 131 class OldStyle:
132 132 """An old-style class for testing."""
133 133 pass
134 134
135 135
136 136 def f(x, y=2, *a, **kw):
137 137 """A simple function."""
138 138
139 139
140 140 def g(y, z=3, *a, **kw):
141 141 pass # no docstring
142 142
143 143
144 144 @register_line_magic
145 145 def lmagic(line):
146 146 "A line magic"
147 147
148 148
149 149 @register_cell_magic
150 150 def cmagic(line, cell):
151 151 "A cell magic"
152 152
153 153
154 154 @register_line_cell_magic
155 155 def lcmagic(line, cell=None):
156 156 "A line/cell magic"
157 157
158 158
159 159 @magics_class
160 160 class SimpleMagics(Magics):
161 161 @line_magic
162 162 def Clmagic(self, cline):
163 163 "A class-based line magic"
164 164
165 165 @cell_magic
166 166 def Ccmagic(self, cline, ccell):
167 167 "A class-based cell magic"
168 168
169 169 @line_cell_magic
170 170 def Clcmagic(self, cline, ccell=None):
171 171 "A class-based line/cell magic"
172 172
173 173
174 174 class Awkward(object):
175 175 def __getattr__(self, name):
176 176 raise Exception(name)
177 177
178 178 class NoBoolCall:
179 179 """
180 180 callable with `__bool__` raising should still be inspect-able.
181 181 """
182 182
183 183 def __call__(self):
184 184 """does nothing"""
185 185 pass
186 186
187 187 def __bool__(self):
188 188 """just raise NotImplemented"""
189 189 raise NotImplementedError('Must be implemented')
190 190
191 191
192 192 class SerialLiar(object):
193 193 """Attribute accesses always get another copy of the same class.
194 194
195 195 unittest.mock.call does something similar, but it's not ideal for testing
196 196 as the failure mode is to eat all your RAM. This gives up after 10k levels.
197 197 """
198 198 def __init__(self, max_fibbing_twig, lies_told=0):
199 199 if lies_told > 10000:
200 200 raise RuntimeError('Nose too long, honesty is the best policy')
201 201 self.max_fibbing_twig = max_fibbing_twig
202 202 self.lies_told = lies_told
203 203 max_fibbing_twig[0] = max(max_fibbing_twig[0], lies_told)
204 204
205 205 def __getattr__(self, item):
206 206 return SerialLiar(self.max_fibbing_twig, self.lies_told + 1)
207 207
208 208
209 209 def check_calltip(obj, name, call, docstring):
210 210 """Generic check pattern all calltip tests will use"""
211 211 info = inspector.info(obj, name)
212 212 call_line, ds = oinspect.call_tip(info)
213 213 nt.assert_equal(call_line, call)
214 214 nt.assert_equal(ds, docstring)
215 215
216 216 #-----------------------------------------------------------------------------
217 217 # Tests
218 218 #-----------------------------------------------------------------------------
219 219
220 220 def test_calltip_class():
221 221 check_calltip(Call, 'Call', 'Call(x, y=1)', Call.__init__.__doc__)
222 222
223 223
224 224 def test_calltip_instance():
225 225 c = Call(1)
226 226 check_calltip(c, 'c', 'c(*a, **kw)', c.__call__.__doc__)
227 227
228 228
229 229 def test_calltip_method():
230 230 c = Call(1)
231 231 check_calltip(c.method, 'c.method', 'c.method(x, z=2)', c.method.__doc__)
232 232
233 233
234 234 def test_calltip_function():
235 235 check_calltip(f, 'f', 'f(x, y=2, *a, **kw)', f.__doc__)
236 236
237 237
238 238 def test_calltip_function2():
239 239 check_calltip(g, 'g', 'g(y, z=3, *a, **kw)', '<no docstring>')
240 240
241 241
242 242 @skipif(sys.version_info >= (3, 5))
243 243 def test_calltip_builtin():
244 244 check_calltip(sum, 'sum', None, sum.__doc__)
245 245
246 246
247 247 def test_calltip_line_magic():
248 248 check_calltip(lmagic, 'lmagic', 'lmagic(line)', "A line magic")
249 249
250 250
251 251 def test_calltip_cell_magic():
252 252 check_calltip(cmagic, 'cmagic', 'cmagic(line, cell)', "A cell magic")
253 253
254 254
255 255 def test_calltip_line_cell_magic():
256 256 check_calltip(lcmagic, 'lcmagic', 'lcmagic(line, cell=None)',
257 257 "A line/cell magic")
258 258
259 259
260 260 def test_class_magics():
261 261 cm = SimpleMagics(ip)
262 262 ip.register_magics(cm)
263 263 check_calltip(cm.Clmagic, 'Clmagic', 'Clmagic(cline)',
264 264 "A class-based line magic")
265 265 check_calltip(cm.Ccmagic, 'Ccmagic', 'Ccmagic(cline, ccell)',
266 266 "A class-based cell magic")
267 267 check_calltip(cm.Clcmagic, 'Clcmagic', 'Clcmagic(cline, ccell=None)',
268 268 "A class-based line/cell magic")
269 269
270 270
271 271 def test_info():
272 272 "Check that Inspector.info fills out various fields as expected."
273 273 i = inspector.info(Call, oname='Call')
274 274 nt.assert_equal(i['type_name'], 'type')
275 275 expted_class = str(type(type)) # <class 'type'> (Python 3) or <type 'type'>
276 276 nt.assert_equal(i['base_class'], expted_class)
277 277 nt.assert_regex(i['string_form'], "<class 'IPython.core.tests.test_oinspect.Call'( at 0x[0-9a-f]{1,9})?>")
278 278 fname = __file__
279 279 if fname.endswith(".pyc"):
280 280 fname = fname[:-1]
281 281 # case-insensitive comparison needed on some filesystems
282 282 # e.g. Windows:
283 283 nt.assert_equal(i['file'].lower(), compress_user(fname).lower())
284 284 nt.assert_equal(i['definition'], None)
285 285 nt.assert_equal(i['docstring'], Call.__doc__)
286 286 nt.assert_equal(i['source'], None)
287 287 nt.assert_true(i['isclass'])
288 288 _self_py2 = '' if py3compat.PY3 else 'self, '
289 289 nt.assert_equal(i['init_definition'], "Call(%sx, y=1)" % _self_py2)
290 290 nt.assert_equal(i['init_docstring'], Call.__init__.__doc__)
291 291
292 292 i = inspector.info(Call, detail_level=1)
293 293 nt.assert_not_equal(i['source'], None)
294 294 nt.assert_equal(i['docstring'], None)
295 295
296 296 c = Call(1)
297 297 c.__doc__ = "Modified instance docstring"
298 298 i = inspector.info(c)
299 299 nt.assert_equal(i['type_name'], 'Call')
300 300 nt.assert_equal(i['docstring'], "Modified instance docstring")
301 301 nt.assert_equal(i['class_docstring'], Call.__doc__)
302 302 nt.assert_equal(i['init_docstring'], Call.__init__.__doc__)
303 303 nt.assert_equal(i['call_docstring'], Call.__call__.__doc__)
304 304
305 305 # Test old-style classes, which for example may not have an __init__ method.
306 306 if not py3compat.PY3:
307 307 i = inspector.info(OldStyle)
308 308 nt.assert_equal(i['type_name'], 'classobj')
309 309
310 310 i = inspector.info(OldStyle())
311 311 nt.assert_equal(i['type_name'], 'instance')
312 312 nt.assert_equal(i['docstring'], OldStyle.__doc__)
313 313
314 314 def test_class_signature():
315 315 info = inspector.info(HasSignature, 'HasSignature')
316 316 nt.assert_equal(info['init_definition'], "HasSignature(test)")
317 317 nt.assert_equal(info['init_docstring'], HasSignature.__init__.__doc__)
318 318
319 319 def test_info_awkward():
320 320 # Just test that this doesn't throw an error.
321 321 inspector.info(Awkward())
322 322
323 323 def test_bool_raise():
324 324 inspector.info(NoBoolCall())
325 325
326 326 def test_info_serialliar():
327 327 fib_tracker = [0]
328 328 inspector.info(SerialLiar(fib_tracker))
329 329
330 330 # Nested attribute access should be cut off at 100 levels deep to avoid
331 331 # infinite loops: https://github.com/ipython/ipython/issues/9122
332 332 nt.assert_less(fib_tracker[0], 9000)
333 333
334 334 def test_calldef_none():
335 335 # We should ignore __call__ for all of these.
336 336 for obj in [f, SimpleClass().method, any, str.upper]:
337 337 print(obj)
338 338 i = inspector.info(obj)
339 339 nt.assert_is(i['call_def'], None)
340 340
341 341 def f_kwarg(pos, *, kwonly):
342 342 pass
343 343
344 344 def test_definition_kwonlyargs():
345 345 i = inspector.info(f_kwarg, oname='f_kwarg') # analysis:ignore
346 346 nt.assert_equal(i['definition'], "f_kwarg(pos, *, kwonly)")
347 347
348 348 def test_getdoc():
349 349 class A(object):
350 350 """standard docstring"""
351 351 pass
352 352
353 353 class B(object):
354 354 """standard docstring"""
355 355 def getdoc(self):
356 356 return "custom docstring"
357 357
358 358 class C(object):
359 359 """standard docstring"""
360 360 def getdoc(self):
361 361 return None
362 362
363 363 a = A()
364 364 b = B()
365 365 c = C()
366 366
367 367 nt.assert_equal(oinspect.getdoc(a), "standard docstring")
368 368 nt.assert_equal(oinspect.getdoc(b), "custom docstring")
369 369 nt.assert_equal(oinspect.getdoc(c), "standard docstring")
370 370
371 371
372 372 def test_empty_property_has_no_source():
373 373 i = inspector.info(property(), detail_level=1)
374 374 nt.assert_is(i['source'], None)
375 375
376 376
377 377 def test_property_sources():
378 378 import zlib
379 379
380 380 class A(object):
381 381 @property
382 382 def foo(self):
383 383 return 'bar'
384 384
385 385 foo = foo.setter(lambda self, v: setattr(self, 'bar', v))
386 386
387 387 id = property(id)
388 388 compress = property(zlib.compress)
389 389
390 390 i = inspector.info(A.foo, detail_level=1)
391 391 nt.assert_in('def foo(self):', i['source'])
392 392 nt.assert_in('lambda self, v:', i['source'])
393 393
394 394 i = inspector.info(A.id, detail_level=1)
395 395 nt.assert_in('fget = <function id>', i['source'])
396 396
397 397 i = inspector.info(A.compress, detail_level=1)
398 398 nt.assert_in('fget = <function zlib.compress>', i['source'])
399 399
400 400
401 401 def test_property_docstring_is_in_info_for_detail_level_0():
402 402 class A(object):
403 403 @property
404 404 def foobar(self):
405 405 """This is `foobar` property."""
406 406 pass
407 407
408 408 ip.user_ns['a_obj'] = A()
409 409 nt.assert_equals(
410 410 'This is `foobar` property.',
411 411 ip.object_inspect('a_obj.foobar', detail_level=0)['docstring'])
412 412
413 413 ip.user_ns['a_cls'] = A
414 414 nt.assert_equals(
415 415 'This is `foobar` property.',
416 416 ip.object_inspect('a_cls.foobar', detail_level=0)['docstring'])
417 417
418 418
419 419 def test_pdef():
420 420 # See gh-1914
421 421 def foo(): pass
422 422 inspector.pdef(foo, 'foo')
423 423
424 424
425 425 def test_pinfo_nonascii():
426 426 # See gh-1177
427 427 from . import nonascii2
428 428 ip.user_ns['nonascii2'] = nonascii2
429 429 ip._inspect('pinfo', 'nonascii2', detail_level=1)
430 430
431 431
432 432 def test_pinfo_magic():
433 433 with AssertPrints('Docstring:'):
434 434 ip._inspect('pinfo', 'lsmagic', detail_level=0)
435 435
436 436 with AssertPrints('Source:'):
437 437 ip._inspect('pinfo', 'lsmagic', detail_level=1)
438 438
439 439
440 440 def test_init_colors():
441 441 # ensure colors are not present in signature info
442 442 info = inspector.info(HasSignature)
443 443 init_def = info['init_definition']
444 444 nt.assert_not_in('[0m', init_def)
445 445
446 446
447 447 def test_builtin_init():
448 448 info = inspector.info(list)
449 449 init_def = info['init_definition']
450 450 # Python < 3.4 can't get init definition from builtins,
451 451 # but still exercise the inspection in case of error-raising bugs.
452 452 if sys.version_info >= (3,4):
453 453 nt.assert_is_not_none(init_def)
454 454
General Comments 0
You need to be logged in to leave comments. Login now