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