##// 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 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 304 class Inspector:
234 305 def __init__(self, color_table=InspectColors,
235 306 code_color_table=PyColorize.ANSICodeColors,
@@ -259,11 +330,11 b' class Inspector:'
259 330 return '%s%s%s' % (self.color_table.active_colors.header,h,
260 331 self.color_table.active_colors.normal)
261 332
262 def set_active_scheme(self,scheme):
333 def set_active_scheme(self, scheme):
263 334 self.color_table.set_active_scheme(scheme)
264 335 self.parser.color_table.set_active_scheme(scheme)
265 336
266 def noinfo(self,msg,oname):
337 def noinfo(self, msg, oname):
267 338 """Generic message when no information is found."""
268 339 print 'No %s found' % msg,
269 340 if oname:
@@ -271,7 +342,7 b' class Inspector:'
271 342 else:
272 343 print
273 344
274 def pdef(self,obj,oname=''):
345 def pdef(self, obj, oname=''):
275 346 """Print the definition header for any callable object.
276 347
277 348 If the object is a class, print the constructor information."""
@@ -366,28 +437,18 b' class Inspector:'
366 437 else:
367 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 441 """Show the whole file where an object was defined."""
371
372 try:
373 try:
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)
442
443 lineno = find_source_lines(obj)
444 if lineno is None:
445 self.noinfo('file', oname)
383 446 return
384 447
385 # We only reach this point if object was successfully queried
386
387 # run contents of file through pager starting at line
388 # where the object is defined
389 ofile = inspect.getabsfile(obj)
390
448 ofile = find_file(obj)
449 # run contents of file through pager starting at line where the object
450 # is defined, as long as the file isn't binary and is actually on the
451 # filesystem.
391 452 if ofile.endswith(('.so', '.dll', '.pyd')):
392 453 print 'File %r is binary, not printing.' % ofile
393 454 elif not os.path.isfile(ofile):
@@ -396,7 +457,7 b' class Inspector:'
396 457 # Print only text files, not extension binaries. Note that
397 458 # getsourcelines returns lineno with 1-offset and page() uses
398 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 462 def _format_fields(self, fields, title_width=12):
402 463 """Formats a list of fields for display.
@@ -570,24 +631,18 b' class Inspector:'
570 631
571 632 # Filename where object was defined
572 633 binary_file = False
573 try:
574 try:
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:
634 fname = find_file(obj)
635 if fname is None:
587 636 # if anything goes wrong, we don't want to show source, so it's as
588 637 # if the file was binary
589 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 646 # reconstruct the function definition and print it:
592 647 defln = self._getdef(obj, oname)
593 648 if defln:
@@ -606,10 +661,10 b' class Inspector:'
606 661 source = None
607 662 try:
608 663 try:
609 source = getsource(obj,binary_file)
664 source = getsource(obj, binary_file)
610 665 except TypeError:
611 if hasattr(obj,'__class__'):
612 source = getsource(obj.__class__,binary_file)
666 if hasattr(obj, '__class__'):
667 source = getsource(obj.__class__, binary_file)
613 668 if source is not None:
614 669 out['source'] = source.rstrip()
615 670 except Exception:
@@ -14,6 +14,7 b''
14 14 from __future__ import print_function
15 15
16 16 # Stdlib imports
17 import os
17 18
18 19 # Third-party imports
19 20 import nose.tools as nt
@@ -37,6 +38,26 b' ip = get_ipython()'
37 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 61 # A few generic objects we can then inspect in the tests below
41 62
42 63 class Call(object):
General Comments 0
You need to be logged in to leave comments. Login now