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 |
|
343 | |||
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