fredrikekre / Literate.jl

Simple package for literate programming in Julia
https://fredrikekre.github.io/Literate.jl
Other
547 stars 64 forks source link

Call `showable` in correct world age #187

Closed devmotion closed 2 years ago

devmotion commented 2 years ago

It took me multiple days to figure out why executing some Markdown blocks that return CairoMakie figures errored even though everything worked when copy-pasted to the REPL. A much much simpler example that demonstrates the problem (requires installation of Literate and CairoMakie):

julia> ] add Literate CairoMakie

julia> using Literate

julia> write("literate_script.jl",
       """
       using CairoMakie
       CairoMakie.activate!(; type="svg")
       fig = lines(rand(3))
       showable(MIME("text/html"), fig) && error("HTML output is not supported")
       fig
       """);

julia> withenv("JULIA_DEBUG" => "Literate") do
           Literate.markdown("literate_script.jl"; execute=true)
       end
[ Info: generating markdown page from ~/literate_script.jl
┌ Debug: execute_block(Main.##291, block)
│
│   using CairoMakie
│   CairoMakie.activate!(; type="svg")
│   fig = lines(rand(3))
│   showable(MIME("text/html"), fig) && error("HTML output is not supported")
│   fig
└ @ Literate /home/david/.julia/packages/Literate/A6l2j/src/Literate.jl:802
ERROR: MethodError: no method matching backend_show(::CairoMakie.CairoBackend, ::IOContext{IOBuffer}, ::MIME{Symbol("text/html")}, ::Makie.Scene)
Closest candidates are:
  backend_show(::Any, ::IO, ::MIME{Symbol("text/plain")}, ::Makie.Scene) at ~/.julia/packages/Makie/14tKQ/src/display.jl:85
  backend_show(::CairoMakie.CairoBackend, ::IO, ::MIME{Symbol("image/png")}, ::Makie.Scene) at ~/.julia/packages/CairoMakie/ozkuo/src/infrastructure.jl:328
  backend_show(::CairoMakie.CairoBackend, ::IO, ::MIME{Symbol("application/postscript")}, ::Makie.Scene) at ~/.julia/packages/CairoMakie/ozkuo/src/infrastructure.jl:317
  ...
Stacktrace:
  [1] show(io::IOBuffer, m::MIME{Symbol("text/html")}, scene::Makie.Scene)
    @ Makie ~/.julia/packages/Makie/14tKQ/src/display.jl:108
  [2] show(io::IOBuffer, m::MIME{Symbol("text/html")}, fig::Makie.Figure; kw::Base.Pairs{Symbol, Union{}, Tuple{}, NamedTuple{(), Tuple{}}})
    @ Makie ~/.julia/packages/Makie/14tKQ/src/display.jl:102
  [3] show(io::IOBuffer, m::MIME{Symbol("text/html")}, fig::Makie.Figure)
    @ Makie ~/.julia/packages/Makie/14tKQ/src/display.jl:102
  [4] show(io::IOBuffer, m::MIME{Symbol("text/html")}, fap::Makie.FigureAxisPlot; kw::Base.Pairs{Symbol, Union{}, Tuple{}, NamedTuple{(), Tuple{}}})
    @ Makie ~/.julia/packages/Makie/14tKQ/src/display.jl:101
  [5] show(io::IOBuffer, m::MIME{Symbol("text/html")}, fap::Makie.FigureAxisPlot)
    @ Makie ~/.julia/packages/Makie/14tKQ/src/display.jl:101
  [6] #invokelatest#2
    @ ./essentials.jl:716 [inlined]
  [7] invokelatest
    @ ./essentials.jl:714 [inlined]
  [8] execute_markdown!(io::IOBuffer, sb::Module, block::String, outputdir::String; inputfile::String, flavor::Literate.DocumenterFlavor, image_formats::Vector{Tuple{MIME, String}})
    @ Literate ~/.julia/packages/Literate/A6l2j/src/Literate.jl:575
  [9] markdown(inputfile::String, outputdir::String; config::Dict{Any, Any}, kwargs::Base.Pairs{Symbol, Bool, Tuple{Symbol}, NamedTuple{(:execute,), Tuple{Bool}}})
    @ Literate ~/.julia/packages/Literate/A6l2j/src/Literate.jl:545
 [10] #3
    @ ./REPL[6]:2 [inlined]
 [11] withenv(f::var"#3#4", keyvals::Pair{String, String})
    @ Base ./env.jl:172
 [12] top-level scope
    @ REPL[6]:1

julia> fig = include("literate_script.jl");

julia> typeof(fig)
Makie.FigureAxisPlot

julia> showable(MIME("text/html"), fig)
false

As I realized after a long time, the problem is that the showable checks in src/Literate.jl run in a different world age than the actual show invocations. Calling showable also with invokelatest fixes the issue.

fredrikekre commented 2 years ago

Thanks

fredrikekre commented 2 years ago

https://github.com/JuliaRegistries/General/pull/54370

devmotion commented 2 years ago

Thanks! I was about to push the following regression test when I noticed that you had already merged and released the PR :slightly_smiling_face: Maybe it could still be useful?

diff --git a/test/runtests.jl b/test/runtests.jl
index 9d34427..1b10962 100644
--- a/test/runtests.jl
+++ b/test/runtests.jl
@@ -790,6 +790,13 @@ end end
                 Base.show(io::IO, mime::MIME"image/svg+xml", ::SVG) = print(io, "SVG")
                 SVG()
                 #-
+                struct SVGOnly end
+                Base.showable(::MIME, ::SVGOnly) = false
+                Base.showable(::MIME"image/svg+xml", ::SVGOnly) = true
+                Base.show(::IO, ::MIME, ::SVGOnly) = error("only SVG output supported")
+                Base.show(io::IO, ::MIME"image/svg+xml", ::SVGOnly) = print(io, "SVGOnly")
+                SVGOnly()
+                #-
                 struct MD end
                 Base.show(io::IO, mime::MIME"text/markdown", ::MD) = print(io, "# " * "MD")
                 Base.show(io::IO, mime::MIME"text/html", ::MD) =
fredrikekre commented 2 years ago

Yea, that would be great.