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