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