lolviz icon indicating copy to clipboard operation
lolviz copied to clipboard

Could we add a "super" display function that chooses the best one based on the datatype?

Open Naereen opened this issue 4 years ago • 11 comments

Hello @parrt! I used your lolviz project a few years ago, and I rediscovered it today. It's awesome!

Could we add a "super" display function that chooses the best one based on the datatype?

When reading the documentation, it shows like 8 different functions, and I don't want to spend my time thinking about the name of one or another function for one or another datatype. What is described in this documentation is almost trivially translated to Python code.

modes = [ "str", "matrix", "call", "calls", "obj", "tree", "lol", "list" ]

def unified_lolviz(obj, mode=None):
    """ Unified function to display `obj` with lolviz, in Jupyter notebook only."""
    if mode == "str" or isinstance(obj):
        return strviz(obj)
    if mode == "matrix" or "<class 'numpy.ndarray'>" == str(type(obj)):
        # can't use isinstance(obj, np.ndarray) without import numpy!
        return matrixviz(obj)
    if mode == "call": return callviz()
    if mode == "calls": return callviz()
    if mode == "lol" or isinstance(obj, list) and obj and isinstance(obj[0], list):
        # obj is a list, is non empty, and obj[0] is a list!
        return lolviz(obj)
    if mode == "list" or isinstance(obj, list):
        return listviz(obj)
    return objviz(obj)  # default

So I'm opening this ticket: if you think this could be added to the library, can we discuss it here, and then I can take care of writing it, testing it, sending a pull-request, and you can merge, and then update on Pypi! What do you think?

Regards from France, @Naereen

Naereen avatar Feb 21 '21 23:02 Naereen

Hi. I believe one of my functions tries to identify the type...maybe objviz(), but sometimes it cannot figure out that the data structure is a tree, versus, say a general graph and so it's necessary to specify treeviz(). I think what you are suggesting is reasonable, but different function names versus a single function with an argument is very similar, right?

parrt avatar Feb 22 '21 20:02 parrt

Hi @parrt :smiley:! Yes, but in my tiny proposal, the optional mode argument is optional! And if implemented correctly (ie with a type hint showing the different options), it can help reduce the number of things to keepin mind:

In[1]: from lolviz import viz
In[2]: mylist = list(range(100)); mystring = "Okay?"; myarray = np.ones((10, 4));
In[3]: viz(mylist); viz(mystring); viz(myarray);
Out[3]: ...

In [5]: viz?
Signature:
viz(
    obj,
    mode:['str', 'matrix', 'call', 'calls', 'obj', 'tree', 'lol', 'list']=None,
)
Docstring: Unified function to display `obj` with lolviz, in Jupyter notebook only.
File:      ~/<ipython-input-3-f2947a4f8b53>
Type:      function
...

I'm writing a tiny notebook to check and improve my "baby" unified version, I'll let you know when it's ready.

Naereen avatar Feb 22 '21 21:02 Naereen

true, but development environments and even jupyter can help you auto complete function names whereas possible parameter values is more challenging. :) Still, it would be nice to have a simpler interface! thanks for the suggestion

parrt avatar Feb 22 '21 21:02 parrt

Capture d’écran_2021-02-22_22-34-03 IPython and Jupyter (and any recent Python IDE) are perfectly able to auto-complete parameter values :+1:

Naereen avatar Feb 22 '21 21:02 Naereen

Well that looks like documentation not auto complete ;)

parrt avatar Feb 22 '21 21:02 parrt

Oh I see what you meant. Hm I guess using an Enum I could get auto-complete like

>>> viz(myobject, mode=<tab>
   | shows different modes

But I won't try, I want to KISS (keep it simple stup*d).

Naereen avatar Feb 22 '21 21:02 Naereen

See this tiny animation, powered by @ipywidgets.interact interactive widget!

Peek 22-02-2021 23-51

It shows the different "mode" of the unified function (I'll give the updated code here, my first version was of course bugged, I wrote it without testing)

Naereen avatar Feb 22 '21 22:02 Naereen

def unified_lolviz(
        obj=None,  # None is allowed, for call/calls mode
        mode:[ "str", "matrix", "call", "calls", "obj", "tree", "lol", "list", "tree" ]=None,
        **kwargs
    ):
    """ Unified function to display `obj` with `lolviz` visualization functions, in Jupyter notebook only.
    
    - it automatically detects the following datatypes: str, list, list of lists, numpy.ndarray (without needing to import it), dict.
    - it tries to display the object cleverly, and fallsback to `lolviz.objviz` if it fails, and then to just returning the object if it fails again (so IPython/jupyter will display it natively, if possible). 
    - mode is one of the following string: "str", "matrix", "call", "calls", "obj", "tree", "lol", "list", "tree", and it will use lolviz.<mode>viz(obj, **kwargs) function.
    """
    try:
        if mode == "str" or isinstance(obj, str):
            return lolviz.strviz(obj, **kwargs)
        elif "<class 'numpy.ndarray'>" == str(type(obj)):
            # can't use isinstance(obj, np.ndarray) without import numpy!
            return lolviz.matrixviz(obj, **kwargs)
        elif mode == "matrix":
            # XXX Experimental transparent use of np.array()
            try:  from numpy import array
            except ImportError: array = lambda obj: obj
            try:
                arrayed_obj = array(obj)
                return lolviz.matrixviz(array_obj, **kwargs)
            except:  # can't convert to numpy.ndarray, just stop trying and return the object
                return obj
        elif mode == "call":
            from sys import _getframe
            return lolviz.callviz(frame=_getframe(1), **kwargs)
            del _getframe
        elif mode == "calls":
            return lolviz.callsviz( **kwargs)
        elif mode == "lol" or isinstance(obj, (tuple, list)) and obj and isinstance(obj[0], (tuple, list)):
            # obj is a list, is non empty, and obj[0] is a list!
            return lolviz.lolviz(obj, **kwargs)
        elif mode == "list" or isinstance(obj, (tuple, list)):
            return lolviz.listviz(obj, **kwargs)
        elif mode == "tree" or isinstance(obj, dict):
            return lolviz.treeviz(obj, **kwargs)  # default
        else:
            return lolviz.objviz(obj, **kwargs)  # default
    except TypeError:
        # unable to use lolviz, let's just return the object,
        # it will be nicely displayed by IPython
        return obj

Naereen avatar Feb 22 '21 23:02 Naereen

@parrt any feedback? Are you interested in adding this?

Naereen avatar Mar 04 '21 01:03 Naereen

Hi @Naereen sorry for the delay. unfortunately this project is not a high priority for me at the moment as I'm running around like crazy putting out fires. I think something like this is fine, where we try to get a generic objviz(...) function as the standard interface. We could try to guess the best visualization if they don't specify one. The callviz() visualization might still be better as a separate function call, by the way, as it really is showing a different thing: the stack versus some object you pass in.

parrt avatar Mar 04 '21 23:03 parrt

Hi @parrt, don't worry. I agree that both callviz and callsviz functions should probably be left out of my proposal.

Naereen avatar Mar 06 '21 10:03 Naereen