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

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

Open
Naereen opened this issue Feb 21, 2021 · 11 comments

Comments

@Naereen
Copy link

Naereen commented Feb 21, 2021

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

@parrt
Copy link
Owner

parrt commented Feb 22, 2021

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?

@Naereen
Copy link
Author

Naereen commented Feb 22, 2021

Hi @parrt 😃!
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.

@parrt
Copy link
Owner

parrt commented Feb 22, 2021

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

@Naereen
Copy link
Author

Naereen commented Feb 22, 2021

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 👍

@parrt
Copy link
Owner

parrt commented Feb 22, 2021

Well that looks like documentation not auto complete ;)

@Naereen
Copy link
Author

Naereen commented Feb 22, 2021

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
Copy link
Author

Naereen commented Feb 22, 2021

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
Copy link
Author

Naereen commented Feb 22, 2021

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 added a commit to Naereen/notebooks that referenced this issue Feb 22, 2021
@Naereen
Copy link
Author

Naereen commented Mar 4, 2021

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

@parrt
Copy link
Owner

parrt commented Mar 4, 2021

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.

@Naereen
Copy link
Author

Naereen commented Mar 6, 2021

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

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

No branches or pull requests

2 participants