JuliaPy / PyPlot.jl

Plotting for Julia based on matplotlib.pyplot
https://github.com/JuliaPy/PyPlot.jl
MIT License
475 stars 87 forks source link

replace isjulia_display with function to fix #476 (PackageCompiler) #478

Closed xzackli closed 4 years ago

xzackli commented 4 years ago

As discussed in #476, the pre- and post-execute IJulia hooks do not execute when PyPlot is included in a sysimage generated by PackageCompiler.jl. This is because the global isjulia_display is set to false -- @marius311 suggests it's because __init__ is called too early. This also causes the hooks to not be pushed to IJulia.

This PR makes things work by replacing isjulia_display with the function isdisplayok(). One must also manually push the hooks, i.e. you create a sysimage with

using PackageCompiler
create_sysimage(
   [:IJulia, :PyPlot]; 
    sysimage_path = "sys.dylib",
    script = "script.jl"
)

where script.jl contains

using IJulia
using PyPlot

Main.IJulia.push_preexecute_hook(PyPlot.force_new_fig)
Main.IJulia.push_postexecute_hook(PyPlot.display_figs)
Main.IJulia.push_posterror_hook(PyPlot.close_figs)

This works but is pretty hacky, so I'd welcome suggestions on how to best solve this.

xzackli commented 4 years ago

I could imagine an alternative solution would be to write a modified version of __init__() which could be called in the notebook, something like

using PyPlot
notebook()

which sets the correct value for the global and adds the hooks if missing, after the IJulia init. This would split the API slightly for those that use sysimages though, which seems really bad.

stevengj commented 4 years ago

I don't think this will fully fix initialization, as I commented in #476.

The only other alternative I can think of is to initialize PyPlot more lazily, e.g. on the first attempt to access a plotting function. But this is potentially a lot of refactoring.