Show More
@@ -0,0 +1,5 b'' | |||||
|
1 | * :func:`IPython.core.oinspect.getsource` call specification has changed: | |||
|
2 | ||||
|
3 | * `oname` keyword argument has been added for property source formatting | |||
|
4 | * `is_binary` keyword argument has been dropped, passing ``True`` had | |||
|
5 | previously short-circuited the function to return ``None`` unconditionally |
@@ -18,6 +18,7 b" __all__ = ['Inspector','InspectColors']" | |||||
18 | import inspect |
|
18 | import inspect | |
19 | import linecache |
|
19 | import linecache | |
20 | import os |
|
20 | import os | |
|
21 | from textwrap import dedent | |||
21 | import types |
|
22 | import types | |
22 | import io as stdlib_io |
|
23 | import io as stdlib_io | |
23 |
|
24 | |||
@@ -28,6 +29,7 b' except ImportError:' | |||||
28 |
|
29 | |||
29 | # IPython's own |
|
30 | # IPython's own | |
30 | from IPython.core import page |
|
31 | from IPython.core import page | |
|
32 | from IPython.lib.pretty import pretty | |||
31 | from IPython.testing.skipdoctest import skip_doctest_py3 |
|
33 | from IPython.testing.skipdoctest import skip_doctest_py3 | |
32 | from IPython.utils import PyColorize |
|
34 | from IPython.utils import PyColorize | |
33 | from IPython.utils import io |
|
35 | from IPython.utils import io | |
@@ -43,7 +45,8 b' from IPython.utils.py3compat import cast_unicode, string_types, PY3' | |||||
43 | _func_call_docstring = types.FunctionType.__call__.__doc__ |
|
45 | _func_call_docstring = types.FunctionType.__call__.__doc__ | |
44 | _object_init_docstring = object.__init__.__doc__ |
|
46 | _object_init_docstring = object.__init__.__doc__ | |
45 | _builtin_type_docstrings = { |
|
47 | _builtin_type_docstrings = { | |
46 |
t.__doc__ for t in (types.ModuleType, types.MethodType, types.FunctionType |
|
48 | t.__doc__ for t in (types.ModuleType, types.MethodType, types.FunctionType, | |
|
49 | property) | |||
47 | } |
|
50 | } | |
48 |
|
51 | |||
49 | _builtin_func_type = type(all) |
|
52 | _builtin_func_type = type(all) | |
@@ -150,33 +153,68 b' def getdoc(obj):' | |||||
150 | return None |
|
153 | return None | |
151 |
|
154 | |||
152 |
|
155 | |||
153 |
def getsource(obj, |
|
156 | def getsource(obj, oname=''): | |
154 | """Wrapper around inspect.getsource. |
|
157 | """Wrapper around inspect.getsource. | |
155 |
|
158 | |||
156 | This can be modified by other projects to provide customized source |
|
159 | This can be modified by other projects to provide customized source | |
157 | extraction. |
|
160 | extraction. | |
158 |
|
161 | |||
159 | Inputs: |
|
162 | Parameters | |
|
163 | ---------- | |||
|
164 | obj : object | |||
|
165 | an object whose source code we will attempt to extract | |||
|
166 | oname : str | |||
|
167 | (optional) a name under which the object is known | |||
160 |
|
168 | |||
161 | - obj: an object whose source code we will attempt to extract. |
|
169 | Returns | |
|
170 | ------- | |||
|
171 | src : unicode or None | |||
162 |
|
172 | |||
163 | Optional inputs: |
|
173 | """ | |
164 |
|
174 | |||
165 | - is_binary: whether the object is known to come from a binary source. |
|
175 | if isinstance(obj, property): | |
166 | This implementation will skip returning any output for binary objects, but |
|
176 | sources = [] | |
167 | custom extractors may know how to meaningfully process them.""" |
|
177 | for attrname in ['fget', 'fset', 'fdel']: | |
|
178 | fn = getattr(obj, attrname) | |||
|
179 | if fn is not None: | |||
|
180 | encoding = get_encoding(fn) | |||
|
181 | oname_prefix = ('%s.' % oname) if oname else '' | |||
|
182 | sources.append(cast_unicode( | |||
|
183 | ''.join(('# ', oname_prefix, attrname)), | |||
|
184 | encoding=encoding)) | |||
|
185 | if inspect.isfunction(fn): | |||
|
186 | sources.append(dedent(getsource(fn))) | |||
|
187 | else: | |||
|
188 | # Default str/repr only prints function name, | |||
|
189 | # pretty.pretty prints module name too. | |||
|
190 | sources.append(cast_unicode( | |||
|
191 | '%s%s = %s\n' % ( | |||
|
192 | oname_prefix, attrname, pretty(fn)), | |||
|
193 | encoding=encoding)) | |||
|
194 | if sources: | |||
|
195 | return '\n'.join(sources) | |||
|
196 | else: | |||
|
197 | return None | |||
168 |
|
198 | |||
169 | if is_binary: |
|
|||
170 | return None |
|
|||
171 | else: |
|
199 | else: | |
172 | # get source if obj was decorated with @decorator |
|
200 | # Get source for non-property objects. | |
173 | if hasattr(obj,"__wrapped__"): |
|
201 | ||
|
202 | # '__wrapped__' attribute is used by some decorators (e.g. ones defined | |||
|
203 | # functools) to provide access to the decorated function. | |||
|
204 | if hasattr(obj, "__wrapped__"): | |||
174 | obj = obj.__wrapped__ |
|
205 | obj = obj.__wrapped__ | |
|
206 | ||||
175 | try: |
|
207 | try: | |
176 | src = inspect.getsource(obj) |
|
208 | src = inspect.getsource(obj) | |
177 | except TypeError: |
|
209 | except TypeError: | |
178 | if hasattr(obj,'__class__'): |
|
210 | # The object itself provided no meaningful source, try looking for | |
179 | src = inspect.getsource(obj.__class__) |
|
211 | # its class definition instead. | |
|
212 | if hasattr(obj, '__class__'): | |||
|
213 | try: | |||
|
214 | src = inspect.getsource(obj.__class__) | |||
|
215 | except TypeError: | |||
|
216 | return None | |||
|
217 | ||||
180 | encoding = get_encoding(obj) |
|
218 | encoding = get_encoding(obj) | |
181 | return cast_unicode(src, encoding=encoding) |
|
219 | return cast_unicode(src, encoding=encoding) | |
182 |
|
220 | |||
@@ -457,15 +495,18 b' class Inspector:' | |||||
457 | else: |
|
495 | else: | |
458 | page.page('\n'.join(lines)) |
|
496 | page.page('\n'.join(lines)) | |
459 |
|
497 | |||
460 | def psource(self,obj,oname=''): |
|
498 | def psource(self, obj, oname=''): | |
461 | """Print the source code for an object.""" |
|
499 | """Print the source code for an object.""" | |
462 |
|
500 | |||
463 | # Flush the source cache because inspect can return out-of-date source |
|
501 | # Flush the source cache because inspect can return out-of-date source | |
464 | linecache.checkcache() |
|
502 | linecache.checkcache() | |
465 | try: |
|
503 | try: | |
466 | src = getsource(obj) |
|
504 | src = getsource(obj, oname=oname) | |
467 | except: |
|
505 | except Exception: | |
468 | self.noinfo('source',oname) |
|
506 | src = None | |
|
507 | ||||
|
508 | if src is None: | |||
|
509 | self.noinfo('source', oname) | |||
469 | else: |
|
510 | else: | |
470 | page.page(self.format(src)) |
|
511 | page.page(self.format(src)) | |
471 |
|
512 | |||
@@ -696,32 +737,25 b' class Inspector:' | |||||
696 | elif fname.endswith('<string>'): |
|
737 | elif fname.endswith('<string>'): | |
697 | fname = 'Dynamically generated function. No source code available.' |
|
738 | fname = 'Dynamically generated function. No source code available.' | |
698 | out['file'] = fname |
|
739 | out['file'] = fname | |
699 |
|
||||
700 | # Docstrings only in detail 0 mode, since source contains them (we |
|
|||
701 | # avoid repetitions). If source fails, we add them back, see below. |
|
|||
702 | if ds and detail_level == 0: |
|
|||
703 | out['docstring'] = ds |
|
|||
704 |
|
740 | |||
705 |
# Original source code for a |
|
741 | # Original source code for a callable, class or property. | |
706 | if detail_level: |
|
742 | if detail_level: | |
707 | # Flush the source cache because inspect can return out-of-date |
|
743 | # Flush the source cache because inspect can return out-of-date | |
708 | # source |
|
744 | # source | |
709 | linecache.checkcache() |
|
745 | linecache.checkcache() | |
710 | source = None |
|
|||
711 | try: |
|
746 | try: | |
712 | try: |
|
747 | if isinstance(obj, property) or not binary_file: | |
713 |
s |
|
748 | src = getsource(obj, oname) | |
714 | except TypeError: |
|
749 | if src is not None: | |
715 | if hasattr(obj, '__class__'): |
|
750 | src = src.rstrip() | |
716 | source = getsource(obj.__class__, binary_file) |
|
751 | out['source'] = src | |
717 | if source is not None: |
|
752 | ||
718 | out['source'] = source.rstrip() |
|
|||
719 | except Exception: |
|
753 | except Exception: | |
720 | pass |
|
754 | pass | |
721 |
|
755 | |||
722 | if ds and source is None: |
|
756 | # Add docstring only if no source is to be shown (avoid repetitions). | |
723 | out['docstring'] = ds |
|
757 | if ds and out.get('source', None) is None: | |
724 |
|
758 | out['docstring'] = ds | ||
725 |
|
759 | |||
726 | # Constructor docstring for classes |
|
760 | # Constructor docstring for classes | |
727 | if inspect.isclass(obj): |
|
761 | if inspect.isclass(obj): | |
@@ -824,7 +858,6 b' class Inspector:' | |||||
824 |
|
858 | |||
825 | return object_info(**out) |
|
859 | return object_info(**out) | |
826 |
|
860 | |||
827 |
|
||||
828 | def psearch(self,pattern,ns_table,ns_search=[], |
|
861 | def psearch(self,pattern,ns_table,ns_search=[], | |
829 | ignore_case=False,show_all=False): |
|
862 | ignore_case=False,show_all=False): | |
830 | """Search namespaces with wildcards for objects. |
|
863 | """Search namespaces with wildcards for objects. |
@@ -313,6 +313,54 b' def test_getdoc():' | |||||
313 | nt.assert_equal(oinspect.getdoc(b), "custom docstring") |
|
313 | nt.assert_equal(oinspect.getdoc(b), "custom docstring") | |
314 | nt.assert_equal(oinspect.getdoc(c), "standard docstring") |
|
314 | nt.assert_equal(oinspect.getdoc(c), "standard docstring") | |
315 |
|
315 | |||
|
316 | ||||
|
317 | def test_empty_property_has_no_source(): | |||
|
318 | i = inspector.info(property(), detail_level=1) | |||
|
319 | nt.assert_is(i['source'], None) | |||
|
320 | ||||
|
321 | ||||
|
322 | def test_property_sources(): | |||
|
323 | import zlib | |||
|
324 | ||||
|
325 | class A(object): | |||
|
326 | @property | |||
|
327 | def foo(self): | |||
|
328 | return 'bar' | |||
|
329 | ||||
|
330 | foo = foo.setter(lambda self, v: setattr(self, 'bar', v)) | |||
|
331 | ||||
|
332 | id = property(id) | |||
|
333 | compress = property(zlib.compress) | |||
|
334 | ||||
|
335 | i = inspector.info(A.foo, detail_level=1) | |||
|
336 | nt.assert_in('def foo(self):', i['source']) | |||
|
337 | nt.assert_in('lambda self, v:', i['source']) | |||
|
338 | ||||
|
339 | i = inspector.info(A.id, detail_level=1) | |||
|
340 | nt.assert_in('fget = <function id>', i['source']) | |||
|
341 | ||||
|
342 | i = inspector.info(A.compress, detail_level=1) | |||
|
343 | nt.assert_in('fget = <function zlib.compress>', i['source']) | |||
|
344 | ||||
|
345 | ||||
|
346 | def test_property_docstring_is_in_info_for_detail_level_0(): | |||
|
347 | class A(object): | |||
|
348 | @property | |||
|
349 | def foobar(): | |||
|
350 | """This is `foobar` property.""" | |||
|
351 | pass | |||
|
352 | ||||
|
353 | ip.user_ns['a_obj'] = A() | |||
|
354 | nt.assert_equals( | |||
|
355 | 'This is `foobar` property.', | |||
|
356 | ip.object_inspect('a_obj.foobar', detail_level=0)['docstring']) | |||
|
357 | ||||
|
358 | ip.user_ns['a_cls'] = A | |||
|
359 | nt.assert_equals( | |||
|
360 | 'This is `foobar` property.', | |||
|
361 | ip.object_inspect('a_cls.foobar', detail_level=0)['docstring']) | |||
|
362 | ||||
|
363 | ||||
316 | def test_pdef(): |
|
364 | def test_pdef(): | |
317 | # See gh-1914 |
|
365 | # See gh-1914 | |
318 | def foo(): pass |
|
366 | def foo(): pass |
General Comments 0
You need to be logged in to leave comments.
Login now