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