# How to remove PyPlot.jl from the distribution of a package that uses it

**URL:** <https://discourse.julialang.org/t/how-to-remove-pyplot-jl-from-the-distribution-of-a-package-that-uses-it/15130>\
**Category:** Visualization\
**Created:** [September 18, 2018, 2:05pm UTC](https://discourse.julialang.org/t/how-to-remove-pyplot-jl-from-the-distribution-of-a-package-that-uses-it/15130 "2018-09-18T14:05:19Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![MartinOtter](https://avatars.discourse-cdn.com/v4/letter/m/87869e/32.png) [@MartinOtter](https://discourse.julialang.org/u/MartinOtter)\
**Post date:** [September 18, 2018, 2:05pm UTC](https://discourse.julialang.org/t/how-to-remove-pyplot-jl-from-the-distribution-of-a-package-that-uses-it/15130/1 "2018-09-18T14:05:19Z")

</div>

Maybe someone can give advice how to remove [PyPlot](https://github.com/JuliaPy/PyPlot.jl) from the distribution of a Julia package in which it is used, in particular from package [ModiaMath](https://github.com/ModiaSim/ModiaMath.jl). So, this means “PyPlot” should not be within the Project.toml file of ModiaMath. The reasons for this requirement are explained [here](https://github.com/ModiaSim/ModiaMath.jl). The behaviour should be:

- If PyPlot is present in the actual environment, ModiaMath.plot(…) makes a PyPlot plot.
- If PyPlot is not in the actual environment, ModiaMath.plot(…) shall give the information message that PyPlot is not installed.

The following test package demonstrates a solution in Julia 1.0.0:

```julia
module TestPlot1

export myplot

const PyPlotAvailable = fill(false,1)

function __init__ ()
    @eval Main begin
        try
            import PyPlot
        catch
            println("... PyPlot not installed (plot commands will be ignored)")
        end
    end

    PyPlotAvailable[1] = isdefined(Main, :PyPlot)
end
   
function myplot(x,y)
    if PyPlotAvailable[1]
        Main.PyPlot.plot(x,y)  
    else
        println("no plot, because PyPlot not installed")
    end
end

```

Once this package is added to the actual environment (]add TestPlot1), then it can be used as:

```julia
   using TestPlot1
   t=collect(0:0.01:10); y=sin.(t)
   myplot(t,y)

```

and either a plot appears (if PyPlot is in the current environment) or an information message is printed.

@ChrisRackauckas [adviced](https://github.com/ModiaSim/ModiaMath.jl/issues/11#issuecomment-422327541) to not use this _unsafe_ implementation but use one that is based on package `Requires.jl`. I tried it, but until now failed to come up with a solution. It should be possible, because package Plots.jl has the feature above and uses `Requires.jl` (but I did not manage to understand how this is done in Plots.jl).

Here is one of my trials:

```julia
module TestPlot2

using Requires

export myplot

const PyPlotAvailable = fill(false,1)

function __init__ ()
    @require PyPlot = "d330b81b-6aea-500a-939a-2ce795aea3ee" begin
        println("... PyPlot will be loaded.")
        @eval begin
            import PyPlot
            global PyPlotAvailable[1] = true
            pyplot_plot(x,y) = PyPlot.plot(x,y)
        end
    end
end
   

function myplot(x,y)
    if PyPlotAvailable[1]
        pyplot_plot(x,y)  
    else
        println("no plot, because PyPlot not installed")
    end
end

end

```

However, this solution requires that `using PyPlot` has to be explicitely given in the REPL. Once this is done a warning message appears:

```julia
julia> using PyPlot
... PyPlot will be loaded.
┌ Warning: Package TestPlot2 does not have PyPlot in its dependencies:
│ - If you have TestPlot2 checked out for development and have
│ added PyPlot as a dependency but haven't updated your primary
│ environment's manifest file, try `Pkg.resolve()`.
│ - Otherwise you may need to report an issue with TestPlot2
└ Loading PyPlot into TestPlot2 from project dependency, future warnings for TestPlot2 are suppressed.

```

To summarize, can someone give advice for a safer implementation and without requiring that the user has to define “using PyPlot” in the REPL and without getting this warning message.

---

<div class="post-metadata">

**Author:** ![dpsanders](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dpsanders/32/3573_2.png) [@dpsanders](https://discourse.julialang.org/u/dpsanders)\
**Post date:** [September 18, 2018, 2:35pm UTC](https://discourse.julialang.org/t/how-to-remove-pyplot-jl-from-the-distribution-of-a-package-that-uses-it/15130/2 "2018-09-18T14:35:42Z")

</div>

I would recommend to use plot recipes with Plots.jl (or soon with Makie) instead:

[https://github.com/JuliaPlots/RecipesBase.jl](https://github.com/JuliaPlots/RecipesBase.jl)

---

<div class="post-metadata">

**Author:** ![MartinOtter](https://avatars.discourse-cdn.com/v4/letter/m/87869e/32.png) [@MartinOtter](https://discourse.julialang.org/u/MartinOtter)\
**Post date:** [September 18, 2018, 2:44pm UTC](https://discourse.julialang.org/t/how-to-remove-pyplot-jl-from-the-distribution-of-a-package-that-uses-it/15130/3 "2018-09-18T14:44:39Z")

</div>

> I would recommend to use plot recipes with Plots.jl (or soon with Makie) instead:

Typical applications of ModiaMath have several plot windows. For example: The first simulation generates three plot windows. The second simulation reuses these windows (clearing their previous content and adding the new results to these windows). This cannot be defined with Plots. Makie does not support multiple windows either but [plans to support it in the future](https://discourse.julialang.org/t/makie-how-to-define-subscenes-and-or-different-plot-windows/15050/3). To summarize, if multiple window support is needed, Plots and Makie are (today) no option.

---

<div class="post-metadata">

**Author:** ![ChrisRackauckas](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/chrisrackauckas/32/77_2.png) [@ChrisRackauckas](https://discourse.julialang.org/u/ChrisRackauckas)\
**Post date:** [September 18, 2018, 3:19pm UTC](https://discourse.julialang.org/t/how-to-remove-pyplot-jl-from-the-distribution-of-a-package-that-uses-it/15130/4 "2018-09-18T15:19:45Z")

</div>

In your init function you don’t need to `@eval begin import PyPlot` since `@require` will already to the importing.

You might want to use `@isdefined` instead of relying on a global boolean.

You can make `function pyplot_plot end` a stub function defined outside of the ` __init__ ()` function that errors and just add dispatches to it in the `@require` block. Then there’s no need for `myplot` since the function will naturally always exist but throw the error when plotting isn’t installed.

> [@dpsanders](#):
>
> I would recommend to use plot recipes with Plots.jl (or soon with Makie) instead:

Some of what they are relying on and need is sadly part of the “never coming to Plots” set of functionality, but is part of the “can be done with Makie” set.

---

<div class="post-metadata">

**Author:** ![MartinOtter](https://avatars.discourse-cdn.com/v4/letter/m/87869e/32.png) [@MartinOtter](https://discourse.julialang.org/u/MartinOtter)\
**Post date:** [September 18, 2018, 3:43pm UTC](https://discourse.julialang.org/t/how-to-remove-pyplot-jl-from-the-distribution-of-a-package-that-uses-it/15130/5 "2018-09-18T15:43:33Z")

</div>

New try with your hints:

```julia
module TestPlot2

using Requires
export myplot

myplot(x,y) = @info "... myplot(...) ignored, since PyPlot not defined in current environment"

function __init__ ()
    @require PyPlot = "d330b81b-6aea-500a-939a-2ce795aea3ee" begin
        myplot(x,y) = PyPlot.plot(x,y)
    end
end

end

```

O.k. this code is nicer and works better. Example

```julia
   using TestPlot2
   using PyPlot

   t=collect(0:0.01:10); y=sin.(t)
   myplot(t,y)

```

gives a PyPlot plot. If `using PyPlot` is not present, the information message is printed that `PyPlot` is missing.

Is it possible to get rid of the `using PyPlot` command? “Somehow” this should be possible, because with `Plots` a `using PyPlot` needs not to be given, only `using Plots`.

---

<div class="post-metadata">

**Author:** ![MartinOtter](https://avatars.discourse-cdn.com/v4/letter/m/87869e/32.png) [@MartinOtter](https://discourse.julialang.org/u/MartinOtter)\
**Post date:** [September 18, 2018, 3:51pm UTC](https://discourse.julialang.org/t/how-to-remove-pyplot-jl-from-the-distribution-of-a-package-that-uses-it/15130/6 "2018-09-18T15:51:44Z")

</div>

Yet another try, by combining the two codes:

```julia
module TestPlot2

using Requires
export myplot

myplot(x,y) = @info "... myplot(...) ignored, since PyPlot not defined in current environment"

function __init__ ()
    @eval Main begin
        try
            import PyPlot
        catch
            println("... PyPlot not installed (plot commands will be ignored)")
        end
    end

    @require PyPlot = "d330b81b-6aea-500a-939a-2ce795aea3ee" begin
        myplot(x,y) = PyPlot.plot(x,y)
    end
end

end

```

This works now as desired.

---

<div class="post-metadata">

**Author:** ![ChrisRackauckas](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/chrisrackauckas/32/77_2.png) [@ChrisRackauckas](https://discourse.julialang.org/u/ChrisRackauckas)\
**Post date:** [September 18, 2018, 3:53pm UTC](https://discourse.julialang.org/t/how-to-remove-pyplot-jl-from-the-distribution-of-a-package-that-uses-it/15130/7 "2018-09-18T15:53:38Z")

</div>

That solution should be good. There is probably something better than the try-catch though, probably some kind of `isdefined` use.

---

<div class="post-metadata">

**Author:** ![MartinOtter](https://avatars.discourse-cdn.com/v4/letter/m/87869e/32.png) [@MartinOtter](https://discourse.julialang.org/u/MartinOtter)\
**Post date:** [October 5, 2018, 9:37am UTC](https://discourse.julialang.org/t/how-to-remove-pyplot-jl-from-the-distribution-of-a-package-that-uses-it/15130/8 "2018-10-05T09:37:23Z")

</div>

Unfortunately, the above approach leads to a warning when TestPlot2 is a package that is imported in another package TestPlot3:

```julia
module TestPlot3
    import TestPlot2
end

```

When starting Juila (1.0.1) and giving the command `import TestPlot3` a warning occurs:

```julia
julia> import TestPlot3
[Info: Recompiling stale cache file HOME\.julia\compiled\v1.0\TestPlot3\VW81c.ji for TestPlot3 [a2f0c1e0-c87b-11e8-286b-c5f3dd565246]
WARNING: eval from module Main to TestPlot3:
Expr(:block, #= Symbol("HOME\.julia\dev\TestPlot2\src\TestPlot2.jl"):10 =#, Expr(:try, Expr(:block, #= Symbol("HOME\.julia\dev\TestPlot2\src\TestPlot2.jl"):11 =#, Expr(:import, Expr(
:., :PyPlot))), false, Expr(:block, #= Symbol("HOME\.julia\dev\TestPlot2\src\TestPlot2.jl"):13 =#, Expr(:call, :println, "... PyPlot not installed (plot commands will be ignored)"))))
  **incremental compilation may be broken for this module**

```

How to get rid of this warning message?

Note, when leaving Julia and entering again and providing the command `import TestPlot3`, then no info message about rescompiling stale cache file is given, so the cached compiled version from the previous session is used; so incremental compilation seems to be o.k. and the warning message seems to be misleading.

---

<div class="post-metadata">

**Author:** ![MartinOtter](https://avatars.discourse-cdn.com/v4/letter/m/87869e/32.png) [@MartinOtter](https://discourse.julialang.org/u/MartinOtter)\
**Post date:** [October 5, 2018, 2:51pm UTC](https://discourse.julialang.org/t/how-to-remove-pyplot-jl-from-the-distribution-of-a-package-that-uses-it/15130/9 "2018-10-05T14:51:07Z")

</div>

I tried:

```julia
module TestPlot2

using Requires
export myplot

myplot(x,y) = println("... myplot(...) ignored, since PyPlot not defined in current environment")

function __init__ ()
    stdout_old = stdout
    stderr_old = stderr
    redirect_stdout()
    redirect_stderr()

    @eval Main begin
        import PyPlot
    end
    redirect_stdout(stdout_old)
    redirect_stderr(stderr_old)

    
    @require PyPlot = "d330b81b-6aea-500a-939a-2ce795aea3ee" begin
        myplot(x,y) = PyPlot.plot(x,y)
    end
end
end

```

When making `import TestPlot3` (TestPlot3 contains an `import TestPlot2`), the output is:

```julia
julia> import TestPlot3
[Info: Recompiling stale cache file HOME\.julia\compiled\v1.0\TestPlot3\VW81c.ji for TestPlot3 [a2f0c1e0-c87b-11e8-286b-c5f3dd565246]
jl_uv_writecb() ERROR: broken pipe EPIPE
jl_uv_writecb() ERROR: broken pipe EPIPE
jl_uv_writecb() ERROR: broken pipe EPIPE
jl_uv_writecb() ERROR: broken pipe EPIPE
jl_uv_writecb() ERROR: broken pipe EPIPE
jl_uv_writecb() ERROR: broken pipe EPIPE
jl_uv_writecb() ERROR: broken pipe EPIPE
jl_uv_writecb() ERROR: broken pipe EPIPE
jl_uv_writecb() ERROR: broken pipe EPIPE
jl_uv_writecb() ERROR: broken pipe EPIPE
jl_uv_writecb() ERROR: broken pipe EPIPE
jl_uv_writecb() ERROR: broken pipe EPIPE
jl_uv_writecb() ERROR: broken pipe EPIPE
jl_uv_writecb() ERROR: broken pipe EPIPE

```

Besides these errors, it seems that plotting works, because the following command is successful:

```julia
TestPlot3.Testplot2.myplot(...)

```

Is there any other way to redirect the output to devnull?

---

<div class="post-metadata">

**Author:** ![MartinOtter](https://avatars.discourse-cdn.com/v4/letter/m/87869e/32.png) [@MartinOtter](https://discourse.julialang.org/u/MartinOtter)\
**Post date:** [October 5, 2018, 5:05pm UTC](https://discourse.julialang.org/t/how-to-remove-pyplot-jl-from-the-distribution-of-a-package-that-uses-it/15130/10 "2018-10-05T17:05:14Z")

</div>

O.k., finally found a solution that seems to work and not producing warning messages:

```julia
module TestPlot2

using Requires
export myplot

myplot(x,y) = println("... myplot(...) ignored, since PyPlot not available")

function __init__ ()
    if !Requires.isprecompiling()  
        @eval Main begin
            try
                import PyPlot
            catch
                println("... PyPlot not available (plot commands will be ignored)")
            end
        end
    end
    
    @require PyPlot = "d330b81b-6aea-500a-939a-2ce795aea3ee" begin
        myplot(x,y) = PyPlot.plot(x,y)
    end
end

end

```
