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