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