##// END OF EJS Templates
Fix finding of file info for magics and decorated functions....
Fernando Perez -
Show More
@@ -230,6 +230,77 b' def call_tip(oinfo, format_call=True):'
230 return call_line, doc
230 return call_line, doc
231
231
232
232
233 def find_file(obj):
234 """Find the absolute path to the file where an object was defined.
235
236 This is essentially a robust wrapper around `inspect.getabsfile`.
237
238 Returns None if no file can be found.
239
240 Parameters
241 ----------
242 obj : any Python object
243
244 Returns
245 -------
246 fname : str
247 The absolute path to the file where the object was defined.
248 """
249 try:
250 fname = inspect.getabsfile(obj)
251 except TypeError:
252 # For an instance, the file that matters is where its class was
253 # declared.
254 if hasattr(obj,'__class__'):
255 try:
256 fname = inspect.getabsfile(obj.__class__)
257 except TypeError:
258 # Can happen for builtins
259 fname = None
260 except:
261 fname = None
262 else:
263 if fname.endswith('<string>') and hasattr(obj, '__wrapped__'):
264 # Analyze decorated functions and methods correctly
265 fname = inspect.getabsfile(obj.__wrapped__)
266 return fname
267
268
269 def find_source_lines(obj):
270 """Find the line number in a file where an object was defined.
271
272 This is essentially a robust wrapper around `inspect.getsourcelines`.
273
274 Returns None if no file can be found.
275
276 Parameters
277 ----------
278 obj : any Python object
279
280 Returns
281 -------
282 lineno : int
283 The line number where the object definition starts.
284 """
285 try:
286 try:
287 lineno = inspect.getsourcelines(obj)[1]
288 except TypeError:
289 # For instances, try the class object like getsource() does
290 if hasattr(obj,'__class__'):
291 lineno = inspect.getsourcelines(obj.__class__)[1]
292 # Adjust the inspected object so getabsfile() below works
293 obj = obj.__class__
294 except IOError:
295 if hasattr(obj, '__wrapped__'):
296 obj = obj.__wrapped__
297 lineno = inspect.getsourcelines(obj)[1]
298 except:
299 return None
300
301 return lineno
302
303
233 class Inspector:
304 class Inspector:
234 def __init__(self, color_table=InspectColors,
305 def __init__(self, color_table=InspectColors,
235 code_color_table=PyColorize.ANSICodeColors,
306 code_color_table=PyColorize.ANSICodeColors,
@@ -369,25 +440,15 b' class Inspector:'
369 def pfile(self,obj,oname=''):
440 def pfile(self, obj, oname=''):
370 """Show the whole file where an object was defined."""
441 """Show the whole file where an object was defined."""
371
442
372 try:
443 lineno = find_source_lines(obj)
373 try:
444 if lineno is None:
374 lineno = inspect.getsourcelines(obj)[1]
375 except TypeError:
376 # For instances, try the class object like getsource() does
377 if hasattr(obj,'__class__'):
378 lineno = inspect.getsourcelines(obj.__class__)[1]
379 # Adjust the inspected object so getabsfile() below works
380 obj = obj.__class__
381 except:
382 self.noinfo('file',oname)
445 self.noinfo('file', oname)
383 return
446 return
384
447
385 # We only reach this point if object was successfully queried
448 ofile = find_file(obj)
386
449 # run contents of file through pager starting at line where the object
387 # run contents of file through pager starting at line
450 # is defined, as long as the file isn't binary and is actually on the
388 # where the object is defined
451 # filesystem.
389 ofile = inspect.getabsfile(obj)
390
391 if ofile.endswith(('.so', '.dll', '.pyd')):
452 if ofile.endswith(('.so', '.dll', '.pyd')):
392 print 'File %r is binary, not printing.' % ofile
453 print 'File %r is binary, not printing.' % ofile
393 elif not os.path.isfile(ofile):
454 elif not os.path.isfile(ofile):
@@ -570,23 +631,17 b' class Inspector:'
570
631
571 # Filename where object was defined
632 # Filename where object was defined
572 binary_file = False
633 binary_file = False
573 try:
634 fname = find_file(obj)
574 try:
635 if fname is None:
575 fname = inspect.getabsfile(obj)
576 except TypeError:
577 # For an instance, the file that matters is where its class was
578 # declared.
579 if hasattr(obj,'__class__'):
580 fname = inspect.getabsfile(obj.__class__)
581 if fname.endswith('<string>'):
582 fname = 'Dynamically generated function. No source code available.'
583 if fname.endswith(('.so', '.dll', '.pyd')):
584 binary_file = True
585 out['file'] = fname
586 except:
587 # if anything goes wrong, we don't want to show source, so it's as
636 # if anything goes wrong, we don't want to show source, so it's as
588 # if the file was binary
637 # if the file was binary
589 binary_file = True
638 binary_file = True
639 else:
640 if fname.endswith(('.so', '.dll', '.pyd')):
641 binary_file = True
642 elif fname.endswith('<string>'):
643 fname = 'Dynamically generated function. No source code available.'
644 out['file'] = fname
590
645
591 # reconstruct the function definition and print it:
646 # reconstruct the function definition and print it:
592 defln = self._getdef(obj, oname)
647 defln = self._getdef(obj, oname)
@@ -14,6 +14,7 b''
14 from __future__ import print_function
14 from __future__ import print_function
15
15
16 # Stdlib imports
16 # Stdlib imports
17 import os
17
18
18 # Third-party imports
19 # Third-party imports
19 import nose.tools as nt
20 import nose.tools as nt
@@ -37,6 +38,26 b' ip = get_ipython()'
37 # Local utilities
38 # Local utilities
38 #-----------------------------------------------------------------------------
39 #-----------------------------------------------------------------------------
39
40
41 # WARNING: since this test checks the line number where a function is
42 # defined, if any code is inserted above, the following line will need to be
43 # updated. Do NOT insert any whitespace between the next line and the function
44 # definition below.
45 THIS_LINE_NUMBER = 45 # Put here the actual number of this line
46 def test_find_source_lines():
47 nt.assert_equal(oinspect.find_source_lines(test_find_source_lines),
48 THIS_LINE_NUMBER+1)
49
50
51 def test_find_file():
52 nt.assert_equal(oinspect.find_file(test_find_file),
53 os.path.abspath(__file__))
54
55
56 def test_find_file_magic():
57 run = ip.find_line_magic('run')
58 nt.assert_not_equal(oinspect.find_file(run), None)
59
60
40 # A few generic objects we can then inspect in the tests below
61 # A few generic objects we can then inspect in the tests below
41
62
42 class Call(object):
63 class Call(object):
General Comments 0
You need to be logged in to leave comments. Login now