##// END OF EJS Templates
Relax string checks in test_pinfo_docstring_dynamic...
Thomas Kluyver -
Show More
@@ -1,589 +1,589 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):
67 with pytest.raises(OSError):
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_bool_raise():
373 def test_pinfo_bool_raise():
374 """
374 """
375 Test that bool method is not called on parent.
375 Test that bool method is not called on parent.
376 """
376 """
377
377
378 class RaiseBool:
378 class RaiseBool:
379 attr = None
379 attr = None
380
380
381 def __bool__(self):
381 def __bool__(self):
382 raise ValueError("pinfo should not access this method")
382 raise ValueError("pinfo should not access this method")
383
383
384 raise_bool = RaiseBool()
384 raise_bool = RaiseBool()
385
385
386 with cleanup_user_ns(raise_bool=raise_bool):
386 with cleanup_user_ns(raise_bool=raise_bool):
387 ip._inspect("pinfo", "raise_bool.attr", detail_level=0)
387 ip._inspect("pinfo", "raise_bool.attr", detail_level=0)
388
388
389
389
390 def test_pinfo_getindex():
390 def test_pinfo_getindex():
391 def dummy():
391 def dummy():
392 """
392 """
393 MARKER
393 MARKER
394 """
394 """
395
395
396 container = [dummy]
396 container = [dummy]
397 with cleanup_user_ns(container=container):
397 with cleanup_user_ns(container=container):
398 with AssertPrints("MARKER"):
398 with AssertPrints("MARKER"):
399 ip._inspect("pinfo", "container[0]", detail_level=0)
399 ip._inspect("pinfo", "container[0]", detail_level=0)
400 assert "container" not in ip.user_ns.keys()
400 assert "container" not in ip.user_ns.keys()
401
401
402
402
403 def test_qmark_getindex():
403 def test_qmark_getindex():
404 def dummy():
404 def dummy():
405 """
405 """
406 MARKER 2
406 MARKER 2
407 """
407 """
408
408
409 container = [dummy]
409 container = [dummy]
410 with cleanup_user_ns(container=container):
410 with cleanup_user_ns(container=container):
411 with AssertPrints("MARKER 2"):
411 with AssertPrints("MARKER 2"):
412 ip.run_cell("container[0]?")
412 ip.run_cell("container[0]?")
413 assert "container" not in ip.user_ns.keys()
413 assert "container" not in ip.user_ns.keys()
414
414
415
415
416 def test_qmark_getindex_negatif():
416 def test_qmark_getindex_negatif():
417 def dummy():
417 def dummy():
418 """
418 """
419 MARKER 3
419 MARKER 3
420 """
420 """
421
421
422 container = [dummy]
422 container = [dummy]
423 with cleanup_user_ns(container=container):
423 with cleanup_user_ns(container=container):
424 with AssertPrints("MARKER 3"):
424 with AssertPrints("MARKER 3"):
425 ip.run_cell("container[-1]?")
425 ip.run_cell("container[-1]?")
426 assert "container" not in ip.user_ns.keys()
426 assert "container" not in ip.user_ns.keys()
427
427
428
428
429
429
430 def test_pinfo_nonascii():
430 def test_pinfo_nonascii():
431 # See gh-1177
431 # See gh-1177
432 from . import nonascii2
432 from . import nonascii2
433 ip.user_ns['nonascii2'] = nonascii2
433 ip.user_ns['nonascii2'] = nonascii2
434 ip._inspect('pinfo', 'nonascii2', detail_level=1)
434 ip._inspect('pinfo', 'nonascii2', detail_level=1)
435
435
436 def test_pinfo_type():
436 def test_pinfo_type():
437 """
437 """
438 type can fail in various edge case, for example `type.__subclass__()`
438 type can fail in various edge case, for example `type.__subclass__()`
439 """
439 """
440 ip._inspect('pinfo', 'type')
440 ip._inspect('pinfo', 'type')
441
441
442
442
443 def test_pinfo_docstring_no_source():
443 def test_pinfo_docstring_no_source():
444 """Docstring should be included with detail_level=1 if there is no source"""
444 """Docstring should be included with detail_level=1 if there is no source"""
445 with AssertPrints('Docstring:'):
445 with AssertPrints('Docstring:'):
446 ip._inspect('pinfo', 'str.format', detail_level=0)
446 ip._inspect('pinfo', 'str.format', detail_level=0)
447 with AssertPrints('Docstring:'):
447 with AssertPrints('Docstring:'):
448 ip._inspect('pinfo', 'str.format', detail_level=1)
448 ip._inspect('pinfo', 'str.format', detail_level=1)
449
449
450
450
451 def test_pinfo_no_docstring_if_source():
451 def test_pinfo_no_docstring_if_source():
452 """Docstring should not be included with detail_level=1 if source is found"""
452 """Docstring should not be included with detail_level=1 if source is found"""
453 def foo():
453 def foo():
454 """foo has a docstring"""
454 """foo has a docstring"""
455
455
456 ip.user_ns['foo'] = foo
456 ip.user_ns['foo'] = foo
457
457
458 with AssertPrints('Docstring:'):
458 with AssertPrints('Docstring:'):
459 ip._inspect('pinfo', 'foo', detail_level=0)
459 ip._inspect('pinfo', 'foo', detail_level=0)
460 with AssertPrints('Source:'):
460 with AssertPrints('Source:'):
461 ip._inspect('pinfo', 'foo', detail_level=1)
461 ip._inspect('pinfo', 'foo', detail_level=1)
462 with AssertNotPrints('Docstring:'):
462 with AssertNotPrints('Docstring:'):
463 ip._inspect('pinfo', 'foo', detail_level=1)
463 ip._inspect('pinfo', 'foo', detail_level=1)
464
464
465
465
466 def test_pinfo_docstring_if_detail_and_no_source():
466 def test_pinfo_docstring_if_detail_and_no_source():
467 """ Docstring should be displayed if source info not available """
467 """ Docstring should be displayed if source info not available """
468 obj_def = '''class Foo(object):
468 obj_def = '''class Foo(object):
469 """ This is a docstring for Foo """
469 """ This is a docstring for Foo """
470 def bar(self):
470 def bar(self):
471 """ This is a docstring for Foo.bar """
471 """ This is a docstring for Foo.bar """
472 pass
472 pass
473 '''
473 '''
474
474
475 ip.run_cell(obj_def)
475 ip.run_cell(obj_def)
476 ip.run_cell('foo = Foo()')
476 ip.run_cell('foo = Foo()')
477
477
478 with AssertNotPrints("Source:"):
478 with AssertNotPrints("Source:"):
479 with AssertPrints('Docstring:'):
479 with AssertPrints('Docstring:'):
480 ip._inspect('pinfo', 'foo', detail_level=0)
480 ip._inspect('pinfo', 'foo', detail_level=0)
481 with AssertPrints('Docstring:'):
481 with AssertPrints('Docstring:'):
482 ip._inspect('pinfo', 'foo', detail_level=1)
482 ip._inspect('pinfo', 'foo', detail_level=1)
483 with AssertPrints('Docstring:'):
483 with AssertPrints('Docstring:'):
484 ip._inspect('pinfo', 'foo.bar', detail_level=0)
484 ip._inspect('pinfo', 'foo.bar', detail_level=0)
485
485
486 with AssertNotPrints('Docstring:'):
486 with AssertNotPrints('Docstring:'):
487 with AssertPrints('Source:'):
487 with AssertPrints('Source:'):
488 ip._inspect('pinfo', 'foo.bar', detail_level=1)
488 ip._inspect('pinfo', 'foo.bar', detail_level=1)
489
489
490
490
491 @pytest.mark.xfail(
491 @pytest.mark.xfail(
492 sys.version_info.releaselevel not in ("final", "candidate"),
492 sys.version_info.releaselevel not in ("final", "candidate"),
493 reason="fails on 3.13.dev",
493 reason="fails on 3.13.dev",
494 strict=True,
494 strict=True,
495 )
495 )
496 def test_pinfo_docstring_dynamic(capsys):
496 def test_pinfo_docstring_dynamic(capsys):
497 obj_def = """class Bar:
497 obj_def = """class Bar:
498 __custom_documentations__ = {
498 __custom_documentations__ = {
499 "prop" : "cdoc for prop",
499 "prop" : "cdoc for prop",
500 "non_exist" : "cdoc for non_exist",
500 "non_exist" : "cdoc for non_exist",
501 }
501 }
502 @property
502 @property
503 def prop(self):
503 def prop(self):
504 '''
504 '''
505 Docstring for prop
505 Docstring for prop
506 '''
506 '''
507 return self._prop
507 return self._prop
508
508
509 @prop.setter
509 @prop.setter
510 def prop(self, v):
510 def prop(self, v):
511 self._prop = v
511 self._prop = v
512 """
512 """
513 ip.run_cell(obj_def)
513 ip.run_cell(obj_def)
514
514
515 ip.run_cell("b = Bar()")
515 ip.run_cell("b = Bar()")
516
516
517 ip.run_line_magic("pinfo", "b.prop")
517 ip.run_line_magic("pinfo", "b.prop")
518 captured = capsys.readouterr()
518 captured = capsys.readouterr()
519 assert "Docstring: cdoc for prop" in captured.out
519 assert re.search(r"Docstring:\s+cdoc for prop", captured.out)
520
520
521 ip.run_line_magic("pinfo", "b.non_exist")
521 ip.run_line_magic("pinfo", "b.non_exist")
522 captured = capsys.readouterr()
522 captured = capsys.readouterr()
523 assert "Docstring: cdoc for non_exist" in captured.out
523 assert re.search(r"Docstring:\s+cdoc for non_exist", captured.out)
524
524
525 ip.run_cell("b.prop?")
525 ip.run_cell("b.prop?")
526 captured = capsys.readouterr()
526 captured = capsys.readouterr()
527 assert "Docstring: cdoc for prop" in captured.out
527 assert re.search(r"Docstring:\s+cdoc for prop", captured.out)
528
528
529 ip.run_cell("b.non_exist?")
529 ip.run_cell("b.non_exist?")
530 captured = capsys.readouterr()
530 captured = capsys.readouterr()
531 assert "Docstring: cdoc for non_exist" in captured.out
531 assert re.search(r"Docstring:\s+cdoc for non_exist", captured.out)
532
532
533 ip.run_cell("b.undefined?")
533 ip.run_cell("b.undefined?")
534 captured = capsys.readouterr()
534 captured = capsys.readouterr()
535 assert "Docstring: <no docstring>" in captured.out
535 assert re.search(r"Type:\s+NoneType", captured.out)
536
536
537
537
538 def test_pinfo_magic():
538 def test_pinfo_magic():
539 with AssertPrints("Docstring:"):
539 with AssertPrints("Docstring:"):
540 ip._inspect("pinfo", "lsmagic", detail_level=0)
540 ip._inspect("pinfo", "lsmagic", detail_level=0)
541
541
542 with AssertPrints("Source:"):
542 with AssertPrints("Source:"):
543 ip._inspect("pinfo", "lsmagic", detail_level=1)
543 ip._inspect("pinfo", "lsmagic", detail_level=1)
544
544
545
545
546 def test_init_colors():
546 def test_init_colors():
547 # ensure colors are not present in signature info
547 # ensure colors are not present in signature info
548 info = inspector.info(HasSignature)
548 info = inspector.info(HasSignature)
549 init_def = info["init_definition"]
549 init_def = info["init_definition"]
550 assert "[0m" not in init_def
550 assert "[0m" not in init_def
551
551
552
552
553 def test_builtin_init():
553 def test_builtin_init():
554 info = inspector.info(list)
554 info = inspector.info(list)
555 init_def = info['init_definition']
555 init_def = info['init_definition']
556 assert init_def is not None
556 assert init_def is not None
557
557
558
558
559 def test_render_signature_short():
559 def test_render_signature_short():
560 def short_fun(a=1): pass
560 def short_fun(a=1): pass
561 sig = oinspect._render_signature(
561 sig = oinspect._render_signature(
562 signature(short_fun),
562 signature(short_fun),
563 short_fun.__name__,
563 short_fun.__name__,
564 )
564 )
565 assert sig == "short_fun(a=1)"
565 assert sig == "short_fun(a=1)"
566
566
567
567
568 def test_render_signature_long():
568 def test_render_signature_long():
569 from typing import Optional
569 from typing import Optional
570
570
571 def long_function(
571 def long_function(
572 a_really_long_parameter: int,
572 a_really_long_parameter: int,
573 and_another_long_one: bool = False,
573 and_another_long_one: bool = False,
574 let_us_make_sure_this_is_looong: Optional[str] = None,
574 let_us_make_sure_this_is_looong: Optional[str] = None,
575 ) -> bool: pass
575 ) -> bool: pass
576
576
577 sig = oinspect._render_signature(
577 sig = oinspect._render_signature(
578 signature(long_function),
578 signature(long_function),
579 long_function.__name__,
579 long_function.__name__,
580 )
580 )
581 expected = """\
581 expected = """\
582 long_function(
582 long_function(
583 a_really_long_parameter: int,
583 a_really_long_parameter: int,
584 and_another_long_one: bool = False,
584 and_another_long_one: bool = False,
585 let_us_make_sure_this_is_looong: Optional[str] = None,
585 let_us_make_sure_this_is_looong: Optional[str] = None,
586 ) -> bool\
586 ) -> bool\
587 """
587 """
588
588
589 assert sig == expected
589 assert sig == expected
General Comments 0
You need to be logged in to leave comments. Login now