##// 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,
@@ -259,11 +330,11 b' class Inspector:'
259 return '%s%s%s' % (self.color_table.active_colors.header,h,
330 return '%s%s%s' % (self.color_table.active_colors.header,h,
260 self.color_table.active_colors.normal)
331 self.color_table.active_colors.normal)
261
332
262 def set_active_scheme(self,scheme):
333 def set_active_scheme(self, scheme):
263 self.color_table.set_active_scheme(scheme)
334 self.color_table.set_active_scheme(scheme)
264 self.parser.color_table.set_active_scheme(scheme)
335 self.parser.color_table.set_active_scheme(scheme)
265
336
266 def noinfo(self,msg,oname):
337 def noinfo(self, msg, oname):
267 """Generic message when no information is found."""
338 """Generic message when no information is found."""
268 print 'No %s found' % msg,
339 print 'No %s found' % msg,
269 if oname:
340 if oname:
@@ -271,7 +342,7 b' class Inspector:'
271 else:
342 else:
272 print
343 print
273
344
274 def pdef(self,obj,oname=''):
345 def pdef(self, obj, oname=''):
275 """Print the definition header for any callable object.
346 """Print the definition header for any callable object.
276
347
277 If the object is a class, print the constructor information."""
348 If the object is a class, print the constructor information."""
@@ -366,28 +437,18 b' class Inspector:'
366 else:
437 else:
367 page.page(self.format(py3compat.unicode_to_str(src)))
438 page.page(self.format(py3compat.unicode_to_str(src)))
368
439
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]
445 self.noinfo('file', oname)
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)
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):
@@ -396,7 +457,7 b' class Inspector:'
396 # Print only text files, not extension binaries. Note that
457 # Print only text files, not extension binaries. Note that
397 # getsourcelines returns lineno with 1-offset and page() uses
458 # getsourcelines returns lineno with 1-offset and page() uses
398 # 0-offset, so we must adjust.
459 # 0-offset, so we must adjust.
399 page.page(self.format(open(ofile).read()),lineno-1)
460 page.page(self.format(open(ofile).read()), lineno-1)
400
461
401 def _format_fields(self, fields, title_width=12):
462 def _format_fields(self, fields, title_width=12):
402 """Formats a list of fields for display.
463 """Formats a list of fields for display.
@@ -570,24 +631,18 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
590
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
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)
593 if defln:
648 if defln:
@@ -606,10 +661,10 b' class Inspector:'
606 source = None
661 source = None
607 try:
662 try:
608 try:
663 try:
609 source = getsource(obj,binary_file)
664 source = getsource(obj, binary_file)
610 except TypeError:
665 except TypeError:
611 if hasattr(obj,'__class__'):
666 if hasattr(obj, '__class__'):
612 source = getsource(obj.__class__,binary_file)
667 source = getsource(obj.__class__, binary_file)
613 if source is not None:
668 if source is not None:
614 out['source'] = source.rstrip()
669 out['source'] = source.rstrip()
615 except Exception:
670 except Exception:
@@ -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