Show More
@@ -79,7 +79,7 b' class NamespaceMagics(Magics):' | |||
|
79 | 79 | In [3]: %pdef urllib.urlopen |
|
80 | 80 | urllib.urlopen(url, data=None, proxies=None) |
|
81 | 81 | """ |
|
82 | self._inspect('pdef',parameter_s, namespaces) | |
|
82 | self.shell._inspect('pdef',parameter_s, namespaces) | |
|
83 | 83 | |
|
84 | 84 | @line_magic |
|
85 | 85 | def pdoc(self, parameter_s='', namespaces=None): |
@@ -87,12 +87,12 b' class NamespaceMagics(Magics):' | |||
|
87 | 87 | |
|
88 | 88 | If the given object is a class, it will print both the class and the |
|
89 | 89 | constructor docstrings.""" |
|
90 | self._inspect('pdoc',parameter_s, namespaces) | |
|
90 | self.shell._inspect('pdoc',parameter_s, namespaces) | |
|
91 | 91 | |
|
92 | 92 | @line_magic |
|
93 | 93 | def psource(self, parameter_s='', namespaces=None): |
|
94 | 94 | """Print (or run through pager) the source code for an object.""" |
|
95 | self._inspect('psource',parameter_s, namespaces) | |
|
95 | self.shell._inspect('psource',parameter_s, namespaces) | |
|
96 | 96 | |
|
97 | 97 | @line_magic |
|
98 | 98 | def pfile(self, parameter_s=''): |
@@ -108,7 +108,7 b' class NamespaceMagics(Magics):' | |||
|
108 | 108 | viewer.""" |
|
109 | 109 | |
|
110 | 110 | # first interpret argument as an object name |
|
111 | out = self._inspect('pfile',parameter_s) | |
|
111 | out = self.shell._inspect('pfile',parameter_s) | |
|
112 | 112 | # if not, try the input as a filename |
|
113 | 113 | if out == 'not found': |
|
114 | 114 | try: |
@@ -230,6 +230,75 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 | # get source if obj was decorated with @decorator | |
|
250 | if hasattr(obj, '__wrapped__'): | |
|
251 | obj = obj.__wrapped__ | |
|
252 | ||
|
253 | try: | |
|
254 | fname = inspect.getabsfile(obj) | |
|
255 | except TypeError: | |
|
256 | # For an instance, the file that matters is where its class was | |
|
257 | # declared. | |
|
258 | if hasattr(obj, '__class__'): | |
|
259 | try: | |
|
260 | fname = inspect.getabsfile(obj.__class__) | |
|
261 | except TypeError: | |
|
262 | # Can happen for builtins | |
|
263 | fname = None | |
|
264 | except: | |
|
265 | fname = None | |
|
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 | # get source if obj was decorated with @decorator | |
|
286 | if hasattr(obj, '__wrapped__'): | |
|
287 | obj = obj.__wrapped__ | |
|
288 | ||
|
289 | try: | |
|
290 | try: | |
|
291 | lineno = inspect.getsourcelines(obj)[1] | |
|
292 | except TypeError: | |
|
293 | # For instances, try the class object like getsource() does | |
|
294 | if hasattr(obj, '__class__'): | |
|
295 | lineno = inspect.getsourcelines(obj.__class__)[1] | |
|
296 | except: | |
|
297 | return None | |
|
298 | ||
|
299 | return lineno | |
|
300 | ||
|
301 | ||
|
233 | 302 | class Inspector: |
|
234 | 303 | def __init__(self, color_table=InspectColors, |
|
235 | 304 | code_color_table=PyColorize.ANSICodeColors, |
@@ -259,11 +328,11 b' class Inspector:' | |||
|
259 | 328 | return '%s%s%s' % (self.color_table.active_colors.header,h, |
|
260 | 329 | self.color_table.active_colors.normal) |
|
261 | 330 | |
|
262 | def set_active_scheme(self,scheme): | |
|
331 | def set_active_scheme(self, scheme): | |
|
263 | 332 | self.color_table.set_active_scheme(scheme) |
|
264 | 333 | self.parser.color_table.set_active_scheme(scheme) |
|
265 | 334 | |
|
266 | def noinfo(self,msg,oname): | |
|
335 | def noinfo(self, msg, oname): | |
|
267 | 336 | """Generic message when no information is found.""" |
|
268 | 337 | print 'No %s found' % msg, |
|
269 | 338 | if oname: |
@@ -271,7 +340,7 b' class Inspector:' | |||
|
271 | 340 | else: |
|
272 | 341 | |
|
273 | 342 | |
|
274 | def pdef(self,obj,oname=''): | |
|
343 | def pdef(self, obj, oname=''): | |
|
275 | 344 | """Print the definition header for any callable object. |
|
276 | 345 | |
|
277 | 346 | If the object is a class, print the constructor information.""" |
@@ -366,28 +435,18 b' class Inspector:' | |||
|
366 | 435 | else: |
|
367 | 436 | page.page(self.format(py3compat.unicode_to_str(src))) |
|
368 | 437 | |
|
369 | def pfile(self,obj,oname=''): | |
|
438 | def pfile(self, obj, oname=''): | |
|
370 | 439 | """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) | |
|
440 | ||
|
441 | lineno = find_source_lines(obj) | |
|
442 | if lineno is None: | |
|
443 | self.noinfo('file', oname) | |
|
383 | 444 | return |
|
384 | 445 | |
|
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 | ||
|
446 | ofile = find_file(obj) | |
|
447 | # run contents of file through pager starting at line where the object | |
|
448 | # is defined, as long as the file isn't binary and is actually on the | |
|
449 | # filesystem. | |
|
391 | 450 | if ofile.endswith(('.so', '.dll', '.pyd')): |
|
392 | 451 | print 'File %r is binary, not printing.' % ofile |
|
393 | 452 | elif not os.path.isfile(ofile): |
@@ -396,7 +455,7 b' class Inspector:' | |||
|
396 | 455 | # Print only text files, not extension binaries. Note that |
|
397 | 456 | # getsourcelines returns lineno with 1-offset and page() uses |
|
398 | 457 | # 0-offset, so we must adjust. |
|
399 | page.page(self.format(open(ofile).read()),lineno-1) | |
|
458 | page.page(self.format(open(ofile).read()), lineno-1) | |
|
400 | 459 | |
|
401 | 460 | def _format_fields(self, fields, title_width=12): |
|
402 | 461 | """Formats a list of fields for display. |
@@ -570,24 +629,18 b' class Inspector:' | |||
|
570 | 629 | |
|
571 | 630 | # Filename where object was defined |
|
572 | 631 | 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: | |
|
632 | fname = find_file(obj) | |
|
633 | if fname is None: | |
|
587 | 634 | # if anything goes wrong, we don't want to show source, so it's as |
|
588 | 635 | # if the file was binary |
|
589 | 636 | binary_file = True |
|
590 | ||
|
637 | else: | |
|
638 | if fname.endswith(('.so', '.dll', '.pyd')): | |
|
639 | binary_file = True | |
|
640 | elif fname.endswith('<string>'): | |
|
641 | fname = 'Dynamically generated function. No source code available.' | |
|
642 | out['file'] = fname | |
|
643 | ||
|
591 | 644 | # reconstruct the function definition and print it: |
|
592 | 645 | defln = self._getdef(obj, oname) |
|
593 | 646 | if defln: |
@@ -606,10 +659,10 b' class Inspector:' | |||
|
606 | 659 | source = None |
|
607 | 660 | try: |
|
608 | 661 | try: |
|
609 | source = getsource(obj,binary_file) | |
|
662 | source = getsource(obj, binary_file) | |
|
610 | 663 | except TypeError: |
|
611 | if hasattr(obj,'__class__'): | |
|
612 | source = getsource(obj.__class__,binary_file) | |
|
664 | if hasattr(obj, '__class__'): | |
|
665 | source = getsource(obj.__class__, binary_file) | |
|
613 | 666 | if source is not None: |
|
614 | 667 | out['source'] = source.rstrip() |
|
615 | 668 | 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 |
@@ -24,8 +25,10 b' from IPython.core.magic import (Magics, magics_class, line_magic,' | |||
|
24 | 25 | cell_magic, line_cell_magic, |
|
25 | 26 | register_line_magic, register_cell_magic, |
|
26 | 27 | register_line_cell_magic) |
|
28 | from IPython.external.decorator import decorator | |
|
27 | 29 | from IPython.utils import py3compat |
|
28 | 30 | |
|
31 | ||
|
29 | 32 | #----------------------------------------------------------------------------- |
|
30 | 33 | # Globals and constants |
|
31 | 34 | #----------------------------------------------------------------------------- |
@@ -37,6 +40,58 b' ip = get_ipython()' | |||
|
37 | 40 | # Local utilities |
|
38 | 41 | #----------------------------------------------------------------------------- |
|
39 | 42 | |
|
43 | # WARNING: since this test checks the line number where a function is | |
|
44 | # defined, if any code is inserted above, the following line will need to be | |
|
45 | # updated. Do NOT insert any whitespace between the next line and the function | |
|
46 | # definition below. | |
|
47 | THIS_LINE_NUMBER = 47 # Put here the actual number of this line | |
|
48 | def test_find_source_lines(): | |
|
49 | nt.assert_equal(oinspect.find_source_lines(test_find_source_lines), | |
|
50 | THIS_LINE_NUMBER+1) | |
|
51 | ||
|
52 | ||
|
53 | def test_find_file(): | |
|
54 | nt.assert_equal(oinspect.find_file(test_find_file), | |
|
55 | os.path.abspath(__file__)) | |
|
56 | ||
|
57 | ||
|
58 | def test_find_file_decorated1(): | |
|
59 | ||
|
60 | @decorator | |
|
61 | def noop1(f): | |
|
62 | def wrapper(): | |
|
63 | return f(*a, **kw) | |
|
64 | return wrapper | |
|
65 | ||
|
66 | @noop1 | |
|
67 | def f(x): | |
|
68 | "My docstring" | |
|
69 | ||
|
70 | nt.assert_equal(oinspect.find_file(f), | |
|
71 | os.path.abspath(__file__)) | |
|
72 | nt.assert_equal(f.__doc__, "My docstring") | |
|
73 | ||
|
74 | ||
|
75 | def test_find_file_decorated2(): | |
|
76 | ||
|
77 | @decorator | |
|
78 | def noop2(f, *a, **kw): | |
|
79 | return f(*a, **kw) | |
|
80 | ||
|
81 | @noop2 | |
|
82 | def f(x): | |
|
83 | "My docstring 2" | |
|
84 | ||
|
85 | nt.assert_equal(oinspect.find_file(f), | |
|
86 | os.path.abspath(__file__)) | |
|
87 | nt.assert_equal(f.__doc__, "My docstring 2") | |
|
88 | ||
|
89 | ||
|
90 | def test_find_file_magic(): | |
|
91 | run = ip.find_line_magic('run') | |
|
92 | nt.assert_not_equal(oinspect.find_file(run), None) | |
|
93 | ||
|
94 | ||
|
40 | 95 | # A few generic objects we can then inspect in the tests below |
|
41 | 96 | |
|
42 | 97 | class Call(object): |
General Comments 0
You need to be logged in to leave comments.
Login now