Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Python object inspector for the Jupyter Notebook #7873

Open
allefeld opened this issue Feb 11, 2020 · 15 comments
Open

Python object inspector for the Jupyter Notebook #7873

allefeld opened this issue Feb 11, 2020 · 15 comments
Milestone

Comments

@allefeld
Copy link

allefeld commented Feb 11, 2020

I'm not entirely sure this is the right place, if not sorry & please redirect me.
It is a mixture between a feature suggestion and a request for comments.

When working with JavaScript, I very much appreciate the object inspector that comes with Chrome's Developer Tools Console:
image

I think it would be great to have something like that for use within a Python notebook.
So I started to make a very rough draft:
inspector

This draft implementation creates HTML for the nested list and shows it using IPython.display.HTML. It also injects some CSS for styling and JavaScript to make list items expandable / collapsable. So far so good.

The problem is that Python object hierarchies are huge (actually infinite, because every object's __class__ is also an object with a __class__, etc.), so I have to limit the hierarchy to a few levels.

A better approach would be to dynamically generate subtrees when items are expanded. For that I would need to make the Python implementation aware of JavaScript click events. I tried ipyevents, but it does not give event target information, and the required ipywidgets.HTML (instead of IPython.display.HTML) apparently messes with my CSS & JS.

Questions:

  • Just to be sure, no such interactive object inspector exists yet, right? I checked but I may have missed something. There is the variable inspector extension, but it doesn't allow exploring object hierarchies in the way I have in mind.

  • What would be the most straightforward way to send detailed JS event information to Python, or to request additional HTML elements from Python? I know that an extension could do this, but is there a way short of that complexity?

  • Any other comments / recommendations?

Thanks!

@jasongrout
Copy link
Contributor

* Just to be sure, no such interactive object inspector exists yet, right? I checked but I may have missed something. There is the variable inspector extension, but it doesn't allow exploring object hierarchies in the way I have in mind.

Good, you saw the existing variable explorer extension. There is also a lot of work going into the debugger extension, which has a variable inspector: https://github.com/jupyterlab/debugger

What would be the most straightforward way to send detailed JS event information to Python, or to request additional HTML elements from Python? I know that an extension could do this, but is there a way short of that complexity?

I would have suggested ipyevents, but apparently you already tried that. Can ipyevents be extended to give target information?

If you do go custom, writing a custom widget extension has the advantage that it may work in other frontends

@allefeld
Copy link
Author

I've asked @mwcraig to comment.

The debugger looks promising. When does it come out of beta? ;)

@jasongrout
Copy link
Contributor

When does it come out of beta? ;)

Not sure. They are working hard on it.

@blois
Copy link

blois commented Feb 12, 2020

+1 to a structured representation of objects, Polynote does a decent job of this for Scala.

I played around with a library to do this in Colab a while back, an example is: https://colab.research.google.com/gist/blois/e432e3aa45de92a09baf9a6644269a0e/copy-of-inspector.ipynb (need to execute the notebook to expand the objects).

All of the code for that one is https://github.com/blois/colab_inspector/tree/master/source/inspector.

@allefeld
Copy link
Author

@blois, thanks, I'll have a look.

@jtpio
Copy link
Member

jtpio commented Feb 14, 2020

Thanks @allefeld for sharing that, it looks good.

For now the variable inspector in the debugger extension (which can be tested on Binder) is still being iterated on. The goal is to keep it language agnostic and use standard calls from the Debug Adapter Protocol (see this JEP) to request variables from the kernel.

There is also a table view, which can be more convenient to inspect other types of variables and for other categories of users.

@allefeld
Copy link
Author

I tried ipyevents, but it does not give event target information

I've asked @mwcraig to comment.

After thinking about this some more, I realized it's not enough to be able to listen to mouse events and get their target. To expand a subtree in response to a click I would need to be able to manipulate the DOM of an ipywidgets.HTML widget to fill in the subtree elements. As far as I can tell, there is no API to do so, and it is impossible to execute JavaScript immediately in a notebook (as opposed to creating a JavaScript cell).

So as far as I can tell, there is no way around creating a new widget, which would need an extension. I tried to follow Building a Custom Widget - Email widget, but I can't even get this example to work (chokes on cell [3]).

Please tell me if I'm wrong.

@allefeld
Copy link
Author

Sorry, for the incessant commenting, I hope this stuff is interesting for someone beside me, at least for future reference.

In the meantime I found ipytree, which provides a customizable tree widget based on jsTree. It allows to listen to events, but unfortunately not to "item open/close" events but only to "item select" events. That is enough to allow me to implement my inspector, albeit in a slightly hacky way.

I also found vdom, which at first sight appeared to be perfect: create a display from HTML elements, and listen to pretty much arbitrary DOM events. Unfortunately I then found that vdom elements are immutable, which means every little change in the display necessitates recreating the whole element hierarchy.

I believe vdom done right, i.e. with modifiable objects, would be a way to allow users to create interactive HTML-based displays with relatively little effort, especially without creating an extension, but which does not involve executing arbitrary JavaScript:

  • represent HTML elements by (Python) objects

  • modification of object properties translates into modification of element attributes and vice versa

  • allow to install listeners for all DOM events

Anyone care to create ipydom? ;)

@dhirschfeld
Copy link
Member

@allefeld - I don't know that it does what you're after but it's kind of neat:
https://github.com/nteract/vdom

@rmorshea
Copy link

rmorshea commented Feb 17, 2020

As mentioned in my response from nteract/vdom#83, my project idom (inspired byvdom) is probably the closest you're going to get to arbitrary DOM manipulation. You can try it out in this notebook.

@allefeld
Copy link
Author

allefeld commented Mar 6, 2020

I created a first draft of a package for an interactive Python object and data inspector for the Jupyter Notebook.

https://github.com/allefeld/ipyinspector

Subnodes are created as parent items are expanded, based on the functionality of ipytree.

It also uses inspect.getattr_static to avoid activating code, and therefore shows properties as descriptors.

Comments welcome! Optimally as issues on the repository.

I'm not sure it makes sense to create a PyPI package for such a small amount of code (~170 lines), so I invite comments on how to best publish this.

@Kreijstal
Copy link

@blois I think google colab removed some functions on the frontend so this example doesn't work anymore.
(too bad, I really enjoyed it when it did.)

@rmorshea
Copy link

rmorshea commented Dec 17, 2020

I'm having trouble getting idom_jupyter to work in Jupyterlab right now, but here's a quick example of an inspector implemented with idom running in the classic notebook:

edit: doesn't work in google colab either :(

import idom
import idom_jupyter
from typing import Mapping, Collection


DEFAULT_STYLE = """
.idom-inspector-key-repr {
    font-weight: bold;
    padding-right: 10px;
    margin-right: 5px;
    border-right: 1px solid black;
}
"""


@idom.component
def Inspector(value, style=DEFAULT_STYLE):
    return idom.html.div(
        {"class": "idom-inspector"},
        idom.html.style(style),
        idom.html.ul(Node("root", value))
    )

@idom.component
def Node(key, value):
    is_open, set_is_open = idom.hooks.use_state(False)
    
    if not is_open:
        fields = {}
    else:
        if isinstance(value, Mapping):
            fields = dict(value)
        elif isinstance(value, Collection) and not isinstance(value, str):
            fields = {i: v for i, v in enumerate(value)}
        elif hasattr(value, "__dict__"):
            fields = {k: v for k, v in value.__dict__.items() if not k.startswith("_")}
        elif hasattr(value, "__slots__"):
            slots = [value.__slots__] if isinstance(value.__slots__, str) else value.__slots__
            fields = {k: getattr(value, k) for k in slots}
        else:
            fields = {}
    
    if is_open:
        disabled = not fields
    else:
        disabled = False

    return idom.html.li(
        idom.html.input(
            {
                "class": "idom-inspector-open-button",
                "type": "checkbox",
                "onClick": lambda event: set_is_open(not is_open),
                "disabled": disabled,
            }
        ),
        idom.html.label(
            {"class": "idom-inspector-repr-container"},
            idom.html.span({"class": "idom-inspector-key-repr"}, str(key)),
            idom.html.span({"class": "idom-inspector-value-repr"}, str(value))
        ),
        idom.html.ul(
            [Node(k, v) for k, v in fields.items()]
        )
    )

image

@blois
Copy link

blois commented Dec 17, 2020

@Kreijstal I just updated my code- it was using a browser feature which has since been removed (custom elements v0).

@rmorshea
Copy link

rmorshea commented Feb 9, 2021

Update: idom works in JupyterLab now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants