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