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.
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 = '<pre>%s</pre>' % '\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('<pre>%s</pre>' % 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 = '<pre>%s</pre>' % '\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
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
c = C()
inspect_widget(c)