#!/usr/bin/env python # coding: utf-8 # ## Inspecting objects with widgets # # This is a quick demo of inspecting # # I use `inspect.signature` from Python 3, and function annotations, # but Python 2 methods can be used to encode/discover the same information. # In[1]: import ipywidgets as W from inspect import signature from IPython.display import display from html import escape ip = get_ipython() def make_type_button(atype): button = W.Button(description=atype.__name__) button.on_click(lambda b: display(inspect_widget(atype))) return button def inspect_widget(obj): attributes = {} methods = {} outer_box = W.VBox() for name in dir(obj): if name.startswith('_'): continue attr = getattr(obj, name) if callable(attr): methods[name] = attr else: attributes[name] = attr children = [] # at the top, put the object's repr and __doc__ in_pre = [escape(str(obj))] doc = getattr(obj, '__doc__', None) if doc: in_pre.append(escape(doc)) html = '
%s' % '\n'.join(in_pre) w = W.HTML(html) children.append(w) # create a section for attributes if attributes: children.append(W.Label("Attributes")) # For each attribute # with .name, value, and type # a button on the type will trigger a new inspection for name in sorted(attributes): attr = attributes[name] label = W.Label('.%s = ' % name) type_button = make_type_button(type(attr)) # clicking the type_button closes the current inspector # and opens a new one type_button.on_click(lambda b: outer_box.close()) data, md = ip.display_formatter.format(attr) if 'text/html' in data: value = W.HTML(data['text/html']) else: value = W.HTML('
%s' % escape(data['text/plain'])) box = W.HBox(children=[ label, value, type_button ]) children.append(box) # create a section for methods if methods: children.append(W.Label("Methods")) # for each method, show signature, docstring # if a return type is found in the function annotation, # then show an inspect button for the return type. for name in sorted(methods): method = methods[name] try: sig = signature(method) got_sig = True except Exception: # failed to get signature sig = '()' got_sig = False in_pre = [str(name) + str(sig)] doc = getattr(method, '__doc__', None) if doc: in_pre.append(doc) html = '
%s' % '\n'.join(escape(s) for s in in_pre) w = W.HTML(html) method_widgets = [w] # get return type from function signature annotation # if we got such a type, create a button to inspect the new type if got_sig and isinstance(sig.return_annotation, type): button = make_type_button(sig.return_annotation) method_widgets.append(button) button.on_click(lambda b: outer_box.close()) box = W.VBox(children=method_widgets) children.append(box) outer_box.children = children return outer_box # In[2]: class A(object): """An A object""" a = '' b = 0 def __init__(self): self.a = 'hello' self.b = 5 def __repr__(self): return "%s()" % (self.__class__.__name__) def foo(self, x) -> int: """foo docstring""" return self.a class B(A): """A B object""" def __init__(self): self.a = A() self.b = 5 def bar(self, y) -> A: """bar docstring""" return self.a class C(A): """A C object""" def __init__(self): self.a = 'hello' self.b = B() def mymethod(self, z) -> B: """mymethod docstring""" return self.b # In[3]: c = C() inspect_widget(c) # In[ ]: # In[ ]: