def test_math(): """Test a docstring with Latex on it""" oinfo = object_info() oinfo['name'] = 'Foo' oinfo['docstring'] = 'This is some math :math:`a^2 = b^2 + c^2`' url = spxy.rich_repr(oinfo) _show_page(url)
def test_no_render_math(): """Test a docstring with Latex on it but without rendering it""" oinfo = object_info() oinfo['name'] = 'Foo' oinfo['docstring'] = 'This is a rational number :math:`\\frac{x}{y}`' dr.options['render_math'] = False url = spxy.rich_repr(oinfo) _show_page(url)
def test_basic(): """Test with an empty context""" oinfo = object_info() oinfo['name'] = 'Foo' oinfo['argspec'] = {} oinfo['docstring'] = 'A test' oinfo['type_name'] = 'Function' url = spxy.rich_repr(oinfo) _show_page(url)
def test_plot(): """Test the outline option""" docstring = """ .. plot:: >>> import matplotlib.pyplot as plt >>> plt.plot([1,2,3], [4,5,6]) """ oinfo = object_info() oinfo['name'] = 'Foo' oinfo['docstring'] = docstring url = spxy.rich_repr(oinfo) _show_page(url)
def info(self, obj, oname='', formatter=None, info=None, detail_level=0): """Compute a dict with detailed information about an object. This overrides the superclass method for two main purposes: * avoid calling str() or repr() on the object * use our custom repr Args: obj: object to inspect. oname: (optional) string reference to this object formatter: (optional) custom docstring formatter info: (optional) previously computed information about obj detail_level: (optional) 0 or 1; 1 means "include more detail" Returns: A dict with information about obj. """ # We want to include the following list of keys for all objects: # * name # * found # * isclass # * string_form # * type_name # # For callables, we want to add a subset of: # * argspec # * call_docstring # * definition # * docstring # * file # * init_definition # * init_docstring # * source_end_line # * source_start_line # * source_definition # # For detail_level 1, we include: # * file # This can be expensive, as the stdlib mechanisms for looking up the file # containing obj may call repr(obj). # # NOTE: These keys should stay in sync with the corresponding list in our # frontend code. # # We want non-None values for: # * isalias # * ismagic # * namespace # # TODO(b/138128444): Handle class_docstring and call_def, or determine that # we're safe ignoring them. obj_type = type(obj) out = { 'name': oname, 'found': True, 'is_class': inspect.isclass(obj), 'string_form': None, # Fill in empty values. 'docstring': None, 'file': None, 'isalias': False, 'ismagic': info.ismagic if info else False, 'namespace': info.namespace if info else '', } if detail_level >= self.str_detail_level: out['string_form'] = _safe_repr(obj) if getattr(info, 'ismagic', False): out['type_name'] = 'Magic function' else: out['type_name'] = obj_type.__name__ # If the object is callable, we want to compute a docstring and related # information. We could exit early, but all the code below is in conditional # blocks, so there's no need. # # We can't simply call into the superclass method, as we need to avoid # (transitively) calling inspect.getfile(): this function will end up # calling repr() on our object. # We want to include a docstring if we don't have source, which happens # when: # * detail_level == 0, or # * detail_level == 1 but we can't find source # So we first try dealing with detail_level == 1, and then set # the docstring if no source is set. if detail_level == 1: # This should only ever happen if the user has asked for source (eg via # `obj??`), so we're OK with potentially calling repr for now. # TODO(b/138128444): Ensure we don't call str() or repr(). source = _getsource(obj) if source is None and hasattr(obj, '__class__'): source = _getsource(obj.__class__) if source is not None: out['source'] = source if 'source' not in out: formatter = formatter or (lambda x: x) docstring = formatter(_getdoc(obj) or '<no docstring>') if docstring: out['docstring'] = docstring if _iscallable(obj): filename = oinspect.find_file(obj) if filename and (filename.endswith( ('.py', '.py3', '.pyc')) or '<ipython-input' in filename): out['file'] = filename line = oinspect.find_source_lines(obj) out['source_start_line'] = line # inspect.getsourcelines exposes the length of the source as well, which # can be used to highlight the entire code block, but find_source_lines # currently does not expose this. For now just highlight the first line. out['source_end_line'] = line # The remaining attributes only apply to classes or callables. if inspect.isclass(obj): # For classes with an __init__, we set init_definition and init_docstring. init = getattr(obj, '__init__', None) if init: init_docstring = _getdoc(init) if init_docstring and init_docstring != _BASE_INIT_DOC: out['init_docstring'] = init_docstring init_def = self._getdef(init, oname) if init_def: out['init_definition'] = init_def src_def = _get_source_definition(init) if src_def: out['source_definition'] = src_def # For classes, the __init__ method is the method invoked on call, but # old-style classes may not have an __init__ method. if init: argspec = _getargspec_dict(init) if argspec: out['argspec'] = argspec elif callable(obj): definition = self._getdef(obj, oname) if definition: out['definition'] = definition src_def = _get_source_definition(obj) if src_def: out['source_definition'] = src_def if not oinspect.is_simple_callable(obj): call_docstring = _getdoc(obj.__call__) if call_docstring and call_docstring != _BASE_CALL_DOC: out['call_docstring'] = call_docstring out['argspec'] = _getargspec_dict(obj) return oinspect.object_info(**out)
def test_empty_oinfo(): """Test with totally empty oinfo""" oinfo = object_info() url = spxy.rich_repr(oinfo) _show_page(url)
def test_no_doc(): oinfo = object_info() oinfo['docstring'] = '<no docstring>' url = spxy.rich_repr(oinfo) _show_page(url)