Show More
@@ -79,7 +79,7 b' class NamespaceMagics(Magics):' | |||||
79 | In [3]: %pdef urllib.urlopen |
|
79 | In [3]: %pdef urllib.urlopen | |
80 | urllib.urlopen(url, data=None, proxies=None) |
|
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 | @line_magic |
|
84 | @line_magic | |
85 | def pdoc(self, parameter_s='', namespaces=None): |
|
85 | def pdoc(self, parameter_s='', namespaces=None): | |
@@ -87,12 +87,12 b' class NamespaceMagics(Magics):' | |||||
87 |
|
87 | |||
88 | If the given object is a class, it will print both the class and the |
|
88 | If the given object is a class, it will print both the class and the | |
89 | constructor docstrings.""" |
|
89 | constructor docstrings.""" | |
90 | self._inspect('pdoc',parameter_s, namespaces) |
|
90 | self.shell._inspect('pdoc',parameter_s, namespaces) | |
91 |
|
91 | |||
92 | @line_magic |
|
92 | @line_magic | |
93 | def psource(self, parameter_s='', namespaces=None): |
|
93 | def psource(self, parameter_s='', namespaces=None): | |
94 | """Print (or run through pager) the source code for an object.""" |
|
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 | @line_magic |
|
97 | @line_magic | |
98 | def pfile(self, parameter_s=''): |
|
98 | def pfile(self, parameter_s=''): | |
@@ -108,7 +108,7 b' class NamespaceMagics(Magics):' | |||||
108 | viewer.""" |
|
108 | viewer.""" | |
109 |
|
109 | |||
110 | # first interpret argument as an object name |
|
110 | # first interpret argument as an object name | |
111 | out = self._inspect('pfile',parameter_s) |
|
111 | out = self.shell._inspect('pfile',parameter_s) | |
112 | # if not, try the input as a filename |
|
112 | # if not, try the input as a filename | |
113 | if out == 'not found': |
|
113 | if out == 'not found': | |
114 | try: |
|
114 | try: |
@@ -230,6 +230,75 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 | # 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 | class Inspector: |
|
302 | class Inspector: | |
234 | def __init__(self, color_table=InspectColors, |
|
303 | def __init__(self, color_table=InspectColors, | |
235 | code_color_table=PyColorize.ANSICodeColors, |
|
304 | code_color_table=PyColorize.ANSICodeColors, | |
@@ -259,11 +328,11 b' class Inspector:' | |||||
259 | return '%s%s%s' % (self.color_table.active_colors.header,h, |
|
328 | return '%s%s%s' % (self.color_table.active_colors.header,h, | |
260 | self.color_table.active_colors.normal) |
|
329 | self.color_table.active_colors.normal) | |
261 |
|
330 | |||
262 | def set_active_scheme(self,scheme): |
|
331 | def set_active_scheme(self, scheme): | |
263 | self.color_table.set_active_scheme(scheme) |
|
332 | self.color_table.set_active_scheme(scheme) | |
264 | self.parser.color_table.set_active_scheme(scheme) |
|
333 | self.parser.color_table.set_active_scheme(scheme) | |
265 |
|
334 | |||
266 | def noinfo(self,msg,oname): |
|
335 | def noinfo(self, msg, oname): | |
267 | """Generic message when no information is found.""" |
|
336 | """Generic message when no information is found.""" | |
268 | print 'No %s found' % msg, |
|
337 | print 'No %s found' % msg, | |
269 | if oname: |
|
338 | if oname: | |
@@ -271,7 +340,7 b' class Inspector:' | |||||
271 | else: |
|
340 | else: | |
272 |
|
341 | |||
273 |
|
342 | |||
274 | def pdef(self,obj,oname=''): |
|
343 | def pdef(self, obj, oname=''): | |
275 | """Print the definition header for any callable object. |
|
344 | """Print the definition header for any callable object. | |
276 |
|
345 | |||
277 | If the object is a class, print the constructor information.""" |
|
346 | If the object is a class, print the constructor information.""" | |
@@ -366,28 +435,18 b' class Inspector:' | |||||
366 | else: |
|
435 | else: | |
367 | page.page(self.format(py3compat.unicode_to_str(src))) |
|
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 | """Show the whole file where an object was defined.""" |
|
439 | """Show the whole file where an object was defined.""" | |
371 |
|
440 | |||
372 | try: |
|
441 | lineno = find_source_lines(obj) | |
373 | try: |
|
442 | if lineno is None: | |
374 | lineno = inspect.getsourcelines(obj)[1] |
|
443 | 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 |
|
444 | return | |
384 |
|
445 | |||
385 | # We only reach this point if object was successfully queried |
|
446 | ofile = find_file(obj) | |
386 |
|
447 | # run contents of file through pager starting at line where the object | ||
387 | # run contents of file through pager starting at line |
|
448 | # is defined, as long as the file isn't binary and is actually on the | |
388 | # where the object is defined |
|
449 | # filesystem. | |
389 | ofile = inspect.getabsfile(obj) |
|
|||
390 |
|
||||
391 | if ofile.endswith(('.so', '.dll', '.pyd')): |
|
450 | if ofile.endswith(('.so', '.dll', '.pyd')): | |
392 | print 'File %r is binary, not printing.' % ofile |
|
451 | print 'File %r is binary, not printing.' % ofile | |
393 | elif not os.path.isfile(ofile): |
|
452 | elif not os.path.isfile(ofile): | |
@@ -396,7 +455,7 b' class Inspector:' | |||||
396 | # Print only text files, not extension binaries. Note that |
|
455 | # Print only text files, not extension binaries. Note that | |
397 | # getsourcelines returns lineno with 1-offset and page() uses |
|
456 | # getsourcelines returns lineno with 1-offset and page() uses | |
398 | # 0-offset, so we must adjust. |
|
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 | def _format_fields(self, fields, title_width=12): |
|
460 | def _format_fields(self, fields, title_width=12): | |
402 | """Formats a list of fields for display. |
|
461 | """Formats a list of fields for display. | |
@@ -570,24 +629,18 b' class Inspector:' | |||||
570 |
|
629 | |||
571 | # Filename where object was defined |
|
630 | # Filename where object was defined | |
572 | binary_file = False |
|
631 | binary_file = False | |
573 | try: |
|
632 | fname = find_file(obj) | |
574 | try: |
|
633 | 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 |
|
634 | # if anything goes wrong, we don't want to show source, so it's as | |
588 | # if the file was binary |
|
635 | # if the file was binary | |
589 | binary_file = True |
|
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 | # reconstruct the function definition and print it: |
|
644 | # reconstruct the function definition and print it: | |
592 | defln = self._getdef(obj, oname) |
|
645 | defln = self._getdef(obj, oname) | |
593 | if defln: |
|
646 | if defln: | |
@@ -606,10 +659,10 b' class Inspector:' | |||||
606 | source = None |
|
659 | source = None | |
607 | try: |
|
660 | try: | |
608 | try: |
|
661 | try: | |
609 | source = getsource(obj,binary_file) |
|
662 | source = getsource(obj, binary_file) | |
610 | except TypeError: |
|
663 | except TypeError: | |
611 | if hasattr(obj,'__class__'): |
|
664 | if hasattr(obj, '__class__'): | |
612 | source = getsource(obj.__class__,binary_file) |
|
665 | source = getsource(obj.__class__, binary_file) | |
613 | if source is not None: |
|
666 | if source is not None: | |
614 | out['source'] = source.rstrip() |
|
667 | out['source'] = source.rstrip() | |
615 | except Exception: |
|
668 | 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 | |
@@ -24,8 +25,10 b' from IPython.core.magic import (Magics, magics_class, line_magic,' | |||||
24 | cell_magic, line_cell_magic, |
|
25 | cell_magic, line_cell_magic, | |
25 | register_line_magic, register_cell_magic, |
|
26 | register_line_magic, register_cell_magic, | |
26 | register_line_cell_magic) |
|
27 | register_line_cell_magic) | |
|
28 | from IPython.external.decorator import decorator | |||
27 | from IPython.utils import py3compat |
|
29 | from IPython.utils import py3compat | |
28 |
|
30 | |||
|
31 | ||||
29 | #----------------------------------------------------------------------------- |
|
32 | #----------------------------------------------------------------------------- | |
30 | # Globals and constants |
|
33 | # Globals and constants | |
31 | #----------------------------------------------------------------------------- |
|
34 | #----------------------------------------------------------------------------- | |
@@ -37,6 +40,58 b' ip = get_ipython()' | |||||
37 | # Local utilities |
|
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 | # A few generic objects we can then inspect in the tests below |
|
95 | # A few generic objects we can then inspect in the tests below | |
41 |
|
96 | |||
42 | class Call(object): |
|
97 | class Call(object): |
General Comments 0
You need to be logged in to leave comments.
Login now