JuliaPy / PyInteract.jl

Julia support for IPython-based interactive widgets
6 stars 1 forks source link

Hook into IPython's display #1

Open twavv opened 5 years ago

twavv commented 5 years ago

We need to forward IPython.display.display to actually send things to the IOPub channel (something in the process is not working right now).

twavv commented 5 years ago

I did this, but I'd still like to be able to use IPython's stuff for sending the final execute_result message that way we don't have to special-case the widget MIME (and try to show it for every PyObject that is shown).

stevengj commented 5 years ago

Why is IPython sending an execute_result? IJulia should be the only thing receiving an execute_request and responding with an execute_result.

IPython's display might be sending display_data.

message that way we don't have to special-case the widget MIME

Similar to how PyCall does it, we just need to define something like:

Base.showable(::MIME"application/vnd.jupyter.widget-view+json", o::PyObject) =
    !ispynull(o) && PyCall.hasproperty(o, "_model_id")
twavv commented 5 years ago

It's not, but hooking into that allows us to not special case widgets. Any object that defines a custom mime type in python should™ just work (this is what I was trying to make possible in my PR to IJulia).

So it's not that ipython is sending the messages, but rather, I'd like for IJulia/PyInteract to hook into the way objects are converted to MIME bundles so that we can send that bundle as part of IJulia's execute request.

stevengj commented 5 years ago

I'd like for IJulia/PyInteract to hook into the way objects are converted to MIME bundles

It used to be that this was just via _repr_html_ methods etcetera, which is what PyObject supports now.

Nowadays IPython allows objects to define _repr_mimebundle_ and _ipython_display_ methods, so I guess we should support those?

twavv commented 5 years ago

I'm partial to just trying to re-use as much of the existing machinery as possible. For reference, this is what the actual code looks like (assuming that my IJulia PR is merged as is):

function IJulia.display_payload(object::PyObject)
    # The payload returned is of form [data, metadata].
    payload = IJuliaInteractiveShell.instance().display_formatter.format(object)
    if payload === nothing || isempty(payload[1])
        return nothing
    end

    # Make sure we keep the `PyObject <...>` printing
    payload[1]["text/plain"] = sprint(show, object)

    return (data=payload[1], metadata=payload[2])
end

No special handling, no dealing with dunder methods. I think the more we stick to this kind of approach, the less weird edge cases we're going to run into.

stevengj commented 5 years ago

The problem with calling IPython.display_formatter is that it creates a dependency on IPython.

A Python package Foo could easily have defined _repr_html_ or _repr_mimebundle_ without depending on IPython directly, and I don't think PyCall should force IPython to be installed in order for Foo's types to display correctly.

twavv commented 5 years ago

We're already depending on IPython though (via ipykernel).

I think all of this stuff can be completely orthogonal to PyCall and the way it displays objects. The display_payload thing would only be for IJulia (which I think is desirable).