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