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