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 | 18 | import inspect |
|
19 | 19 | import linecache |
|
20 | 20 | import os |
|
21 | from textwrap import dedent | |
|
21 | 22 | import types |
|
22 | 23 | import io as stdlib_io |
|
23 | 24 | |
@@ -28,6 +29,7 b' except ImportError:' | |||
|
28 | 29 | |
|
29 | 30 | # IPython's own |
|
30 | 31 | from IPython.core import page |
|
32 | from IPython.lib.pretty import pretty | |
|
31 | 33 | from IPython.testing.skipdoctest import skip_doctest_py3 |
|
32 | 34 | from IPython.utils import PyColorize |
|
33 | 35 | from IPython.utils import io |
@@ -43,7 +45,8 b' from IPython.utils.py3compat import cast_unicode, string_types, PY3' | |||
|
43 | 45 | _func_call_docstring = types.FunctionType.__call__.__doc__ |
|
44 | 46 | _object_init_docstring = object.__init__.__doc__ |
|
45 | 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 | 52 | _builtin_func_type = type(all) |
@@ -150,33 +153,68 b' def getdoc(obj):' | |||
|
150 | 153 | return None |
|
151 | 154 | |
|
152 | 155 | |
|
153 |
def getsource(obj, |
|
|
156 | def getsource(obj, oname=''): | |
|
154 | 157 | """Wrapper around inspect.getsource. |
|
155 | 158 | |
|
156 | 159 | This can be modified by other projects to provide customized source |
|
157 | 160 | extraction. |
|
158 | 161 | |
|
159 | Inputs: | |
|
160 | ||
|
161 | - obj: an object whose source code we will attempt to extract. | |
|
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 | |
|
162 | 168 | |
|
163 | Optional inputs: | |
|
169 | Returns | |
|
170 | ------- | |
|
171 | src : unicode or None | |
|
164 | 172 | |
|
165 | - is_binary: whether the object is known to come from a binary source. | |
|
166 | This implementation will skip returning any output for binary objects, but | |
|
167 | custom extractors may know how to meaningfully process them.""" | |
|
173 | """ | |
|
168 | 174 | |
|
169 |
if is |
|
|
175 | if isinstance(obj, property): | |
|
176 | sources = [] | |
|
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: | |
|
170 | 197 | return None |
|
198 | ||
|
171 | 199 | else: |
|
172 | # get source if obj was decorated with @decorator | |
|
200 | # Get source for non-property objects. | |
|
201 | ||
|
202 | # '__wrapped__' attribute is used by some decorators (e.g. ones defined | |
|
203 | # functools) to provide access to the decorated function. | |
|
173 | 204 | if hasattr(obj,"__wrapped__"): |
|
174 | 205 | obj = obj.__wrapped__ |
|
206 | ||
|
175 | 207 | try: |
|
176 | 208 | src = inspect.getsource(obj) |
|
177 | 209 | except TypeError: |
|
210 | # The object itself provided no meaningful source, try looking for | |
|
211 | # its class definition instead. | |
|
178 | 212 | if hasattr(obj,'__class__'): |
|
213 | try: | |
|
179 | 214 | src = inspect.getsource(obj.__class__) |
|
215 | except TypeError: | |
|
216 | return None | |
|
217 | ||
|
180 | 218 | encoding = get_encoding(obj) |
|
181 | 219 | return cast_unicode(src, encoding=encoding) |
|
182 | 220 | |
@@ -463,8 +501,11 b' class Inspector:' | |||
|
463 | 501 | # Flush the source cache because inspect can return out-of-date source |
|
464 | 502 | linecache.checkcache() |
|
465 | 503 | try: |
|
466 | src = getsource(obj) | |
|
467 | except: | |
|
504 | src = getsource(obj, oname=oname) | |
|
505 | except Exception: | |
|
506 | src = None | |
|
507 | ||
|
508 | if src is None: | |
|
468 | 509 | self.noinfo('source',oname) |
|
469 | 510 | else: |
|
470 | 511 | page.page(self.format(src)) |
@@ -697,32 +738,25 b' class Inspector:' | |||
|
697 | 738 | fname = 'Dynamically generated function. No source code available.' |
|
698 | 739 | out['file'] = fname |
|
699 | 740 | |
|
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 | ||
|
705 | # Original source code for any callable | |
|
741 | # Original source code for a callable, class or property. | |
|
706 | 742 | if detail_level: |
|
707 | 743 | # Flush the source cache because inspect can return out-of-date |
|
708 | 744 | # source |
|
709 | 745 | linecache.checkcache() |
|
710 | source = None | |
|
711 | 746 | try: |
|
712 | try: | |
|
713 |
s |
|
|
714 | except TypeError: | |
|
715 | if hasattr(obj, '__class__'): | |
|
716 | source = getsource(obj.__class__, binary_file) | |
|
717 | if source is not None: | |
|
718 | out['source'] = source.rstrip() | |
|
747 | if isinstance(obj, property) or not binary_file: | |
|
748 | src = getsource(obj, oname) | |
|
749 | if src is not None: | |
|
750 | src = src.rstrip() | |
|
751 | out['source'] = src | |
|
752 | ||
|
719 | 753 | except Exception: |
|
720 | 754 | pass |
|
721 | 755 | |
|
722 | if ds and source is None: | |
|
756 | # Add docstring only if no source is to be shown (avoid repetitions). | |
|
757 | if ds and out.get('source', None) is None: | |
|
723 | 758 |
|
|
724 | 759 | |
|
725 | ||
|
726 | 760 | # Constructor docstring for classes |
|
727 | 761 | if inspect.isclass(obj): |
|
728 | 762 | out['isclass'] = True |
@@ -824,7 +858,6 b' class Inspector:' | |||
|
824 | 858 | |
|
825 | 859 | return object_info(**out) |
|
826 | 860 | |
|
827 | ||
|
828 | 861 | def psearch(self,pattern,ns_table,ns_search=[], |
|
829 | 862 | ignore_case=False,show_all=False): |
|
830 | 863 | """Search namespaces with wildcards for objects. |
@@ -313,6 +313,54 b' def test_getdoc():' | |||
|
313 | 313 | nt.assert_equal(oinspect.getdoc(b), "custom docstring") |
|
314 | 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 | 364 | def test_pdef(): |
|
317 | 365 | # See gh-1914 |
|
318 | 366 | def foo(): pass |
General Comments 0
You need to be logged in to leave comments.
Login now