first version

This commit is contained in:
2021-10-23 19:53:47 +02:00
parent f06a24f5cb
commit 25462f27d3
3 changed files with 363 additions and 0 deletions

98
inspector.py Normal file
View File

@ -0,0 +1,98 @@
import inspect
import re
import weakref
import ipywidgets
HEADER = '<div class="rendered_html jp-RenderedHTMLCommon"><table><thead><tr><th>Name</th><th>Type</th><th>Value</th></tr></thead><tr><td>'
FOOTER = '</td></tr></table></div>'
SEP = '</td></tr><tr><td>'
LINE = '{0}</td><td>{1}</td><td>{2}'
IGNORE = ["In", "Out", "exit", "quit", "get_ipython"]
RE_DIGITS = re.compile("([0-9]+)")
class Singleton(type):
def __init__(cls, name, bases, namespace):
super().__init__(name, bases, namespace) # creates the class
cls.__signature__ = inspect.signature(cls.__init__) # restore the constructor signature (instead of that of __call__ below)
cls.__instance__ = lambda: None # fake dead weakref
def __call__(cls, *args, **kwargs):
inst = cls.__instance__()
if not inst:
inst = super().__call__(*args, **kwargs) # creates the instance (calls __new__ and __init__ methods)
cls.__instance__ = weakref.ref(inst)
return inst
class VariableInspector(object, metaclass=Singleton):
def __init__(self, ipython=None):
self._box = ipywidgets.Box()
self._box.layout.overflow_y = "scroll"
self._table = ipywidgets.HTML(value="Loading...")
self._box.children = [self._table]
self._ipython = ipython or get_ipython()
self.start()
def start(self):
"""Add update callback if not already registered"""
if self.is_running:
raise RuntimeError("Cannot start. Update callback is already registered")
self._ipython.events.register("post_run_cell", self._update)
def stop(self):
"""Remove update callback if registered"""
if not self.is_running:
raise RuntimeError("Cannot stop. Update callback is not registered")
self._ipython.events.unregister("post_run_cell", self._update)
@property
def is_running(self):
"""Test if update callback is registered"""
return self._update in self._ipython.events.callbacks["post_run_cell"]
def _update(self, _):
"""Fill table with variable information"""
namespace = self._ipython.user_ns.items()
lines = (format_line(k, v) for k, v in sorted_naturally(namespace) if is_good_entry(k, v))
self._table.value = HEADER + SEP.join(lines) + FOOTER
def _ipython_display_(self):
"""Called when display() or pyout is used to display the Variable Inspector"""
try:
self._box._ipython_display_()
except:
pass
def format_line(k, v):
return LINE.format(k, typename(v), v)
def sorted_naturally(iterable, reverse=False):
natural = lambda item: [int(c) if c.isdigit() else c.casefold() for c in RE_DIGITS.split(str(item))]
return sorted(iterable, key=natural, reverse=reverse)
def is_good_entry(k, v):
return not k.startswith("_") and k not in IGNORE and not isinstance(v, VariableInspector)
def typename(obj):
return type(obj).__name__
inspector = VariableInspector()