Skip to content

Commit bd4afe7

Browse files
author
cnliao
committed
refactor mime support
1 parent 7b00bb5 commit bd4afe7

File tree

2 files changed

+99
-58
lines changed

2 files changed

+99
-58
lines changed

src/execute_request.jl

Lines changed: 93 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -5,52 +5,109 @@
55
import Base.Libc: flush_cstdio
66
import Pkg
77

8-
const text_plain = MIME("text/plain")
9-
const image_svg = MIME("image/svg+xml")
10-
const image_png = MIME("image/png")
11-
const image_jpeg = MIME("image/jpeg")
12-
const text_markdown = MIME("text/markdown")
13-
const text_html = MIME("text/html")
14-
const text_latex = MIME("text/latex") # Jupyter expects this
15-
const text_latex2 = MIME("application/x-latex") # but this is more standard?
16-
const application_vnd_vega_v3 = MIME("application/vnd.vega.v3+json")
17-
const application_vnd_vegalite_v2 = MIME("application/vnd.vegalite.v2+json")
18-
const application_vnd_dataresource = MIME("application/vnd.dataresource+json")
8+
Base.showable(a::AbstractVector{<:MIME}, x) = any(m -> showable(m, x), a)
9+
10+
"""
11+
A vector of MIME types (or vectors of MIME types) that IJulia will try to
12+
render. IJulia will try to render every MIME type specified in the first level
13+
of the vector. If a vector of MIME types is specified, IJulia will include only
14+
the first MIME type that is renderable (this allows for the expression of
15+
priority and exclusion of redundant data).
16+
17+
For example, since "text/plain" is specified as a first-child of the array,
18+
IJulia will always try to include a "text/plain" representation of anything that
19+
is displayed. Since markdown and latex are specified within a sub-vector, IJulia
20+
will always try to render "text/markdown", and will only try to render
21+
"text/latex" if markdown isn't possible.
22+
"""
23+
const ijulia_mime_types = Vector{Union{MIME, AbstractVector{MIME}}}()
24+
25+
register_mime(x::Union{M, AbstractVector{M}}) where {M <: MIME} = push!(ijulia_mime_types, x)
1926

2027
include("magics.jl")
2128

2229
# return a String=>Any dictionary to attach as metadata
2330
# in Jupyter display_data and pyout messages
2431
metadata(x) = Dict()
2532

26-
# return a String=>String dictionary of mimetype=>data
27-
# for passing to Jupyter display_data and execute_result messages.
28-
function display_dict(x)
29-
data = Dict{String,Any}("text/plain" => limitstringmime(text_plain, x))
30-
if showable(application_vnd_vegalite_v2, x)
31-
data[string(application_vnd_vegalite_v2)] = JSON.JSONText(limitstringmime(application_vnd_vegalite_v2, x))
32-
elseif showable(application_vnd_vega_v3, x) # don't send vega if we have vega-lite
33-
data[string(application_vnd_vega_v3)] = JSON.JSONText(limitstringmime(application_vnd_vega_v3, x))
34-
end
35-
if showable(application_vnd_dataresource, x)
36-
data[string(application_vnd_dataresource)] = JSON.JSONText(limitstringmime(application_vnd_dataresource, x))
33+
"""
34+
Generate the preferred MIME representation of x.
35+
36+
Returns a tuple with the selected MIME type and the representation of the data
37+
using that MIME type.
38+
"""
39+
function display_mimestring(mime_array::AbstractVector{MIME}, x)
40+
for m in mime_array
41+
if showable(mime_array, x)
42+
return display_mimestring(m, x)
43+
end
3744
end
38-
if showable(image_svg, x)
39-
data[string(image_svg)] = limitstringmime(image_svg, x)
45+
error("No displayable MIME types in mime array.")
46+
end
47+
48+
abstract type MIMEStringType end
49+
display_mimestring(m::MIME, x) = display_mimestring(m, mimestringtype(m), x)
50+
51+
"""
52+
If false then data of the mime type should be sent to ipython in b64-encoded string.
53+
If true then it will be sent as is.
54+
Defaults to the value of istextmime.
55+
"""
56+
_istextmime(m::MIME) = istextmime(m)
57+
58+
struct RawMIMEString <: MIMEStringType end
59+
for m in [
60+
MIME("text/plain"),
61+
MIME("image/svg+xml"),
62+
[MIME("image/png"),MIME("image/jpeg")],
63+
[
64+
MIME("text/markdown"),
65+
MIME("text/html"),
66+
MIME("text/latex"), # Jupyter expects this
67+
MIME("application/x-latex"), # but this is more standard?
68+
],
69+
]
70+
register_mime(m)
71+
if m isa MIME
72+
@eval mimestringtype(::$m) = RawMIMEString
73+
else
74+
for _m in m
75+
@eval mimestringtype(::$m) = RawMIMEString
76+
end
4077
end
41-
if showable(image_png, x)
42-
data[string(image_png)] = limitstringmime(image_png, x)
43-
elseif showable(image_jpeg, x) # don't send jpeg if we have png
44-
data[string(image_jpeg)] = limitstringmime(image_jpeg, x)
78+
end
79+
display_mimestring(m::MIME, ::RawMIMEString, x) = (m, limitstringmime(m, x))
80+
81+
struct JSONMIMEString <: MIMEStringType end
82+
for m in [
83+
[MIME("application/vnd.vegalite.v2+json"), MIME("application/vnd.vega.v3+json")],
84+
MIME("application/vnd.dataresource+json")
85+
]
86+
register_mime(m)
87+
if m <: MIME
88+
@eval mimestringtype(::$m) = JSONMIMEString
89+
@eval _istextmime(::$m) = true
90+
else
91+
for _m in m
92+
@eval mimestringtype(::$m) = JSONMIMEString
93+
@eval _istextmime(::$m) = true
94+
end
4595
end
46-
if showable(text_markdown, x)
47-
data[string(text_markdown)] = limitstringmime(text_markdown, x)
48-
elseif showable(text_html, x)
49-
data[string(text_html)] = limitstringmime(text_html, x)
50-
elseif showable(text_latex, x)
51-
data[string(text_latex)] = limitstringmime(text_latex, x)
52-
elseif showable(text_latex2, x)
53-
data[string(text_latex)] = limitstringmime(text_latex2, x)
96+
end
97+
display_mimestring(m::MIME, ::JSONMIMEString, x) = (m, JSON.JSONText(limitstringmime(m, x)))
98+
99+
"""
100+
Generate a dictionary of `mime_type => data` pairs for all registered MIME
101+
types. This is the format that Jupyter expects in display_data and
102+
execute_result messages.
103+
"""
104+
function display_dict(x)
105+
data = Dict{String, Union{String, JSONText}}()
106+
for m in ijulia_mime_types
107+
if showable(m, x)
108+
mime, mime_repr = display_mimestring(m, x)
109+
data[string(mime)] = mime_repr
110+
end
54111
end
55112
return data
56113
end

src/inline.jl

Lines changed: 6 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,6 @@ import Base: display, redisplay
22

33
struct InlineDisplay <: AbstractDisplay end
44

5-
# supported MIME types for inline display in IPython, in descending order
6-
# of preference (descending "richness")
7-
const ipy_mime = [
8-
"application/vnd.dataresource+json",
9-
"application/vnd.vegalite.v2+json",
10-
"application/vnd.vega.v3+json",
11-
"text/html",
12-
"text/latex",
13-
"image/svg+xml",
14-
"image/png",
15-
"image/jpeg",
16-
"text/plain",
17-
"text/markdown",
18-
"application/javascript"
19-
]
20-
215
# need special handling for showing a string as a textmime
226
# type, since in that case the string is assumed to be
237
# raw data unless it is text/plain
@@ -29,7 +13,7 @@ israwtext(::MIME, x) = false
2913
# IOContext that tells the underlying show function to limit output
3014
function limitstringmime(mime::MIME, x)
3115
buf = IOBuffer()
32-
if istextmime(mime)
16+
if _istextmime(mime)
3317
if israwtext(mime, x)
3418
return String(x)
3519
else
@@ -47,14 +31,14 @@ function limitstringmime(mime::MIME, x)
4731
return String(take!(buf))
4832
end
4933

50-
for mime in ipy_mime
34+
for mime in ijulia_mime_types
5135
@eval begin
52-
function display(d::InlineDisplay, ::MIME{Symbol($mime)}, x)
36+
function display(d::InlineDisplay, m::MIME{Symbol($mime)}, x)
5337
send_ipython(publish[],
5438
msg_pub(execute_msg, "display_data",
5539
Dict(
5640
"metadata" => metadata(x), # optional
57-
"data" => Dict($mime => limitstringmime(MIME($mime), x)))))
41+
"data" => Dict($mime=>display_mimestring(m, x)[2]))))
5842
end
5943
displayable(d::InlineDisplay, ::MIME{Symbol($mime)}) = true
6044
end
@@ -68,11 +52,11 @@ display(d::InlineDisplay, m::MIME"text/javascript", x) = display(d, MIME("applic
6852

6953
# If the user explicitly calls display("foo/bar", x), we send
7054
# the display message, also sending text/plain for text data.
71-
displayable(d::InlineDisplay, M::MIME) = istextmime(M)
55+
displayable(d::InlineDisplay, M::MIME) = _istextmime(M)
7256
function display(d::InlineDisplay, M::MIME, x)
7357
sx = limitstringmime(M, x)
7458
d = Dict(string(M) => sx)
75-
if istextmime(M)
59+
if _istextmime(M)
7660
d["text/plain"] = sx # directly show text data, e.g. text/csv
7761
end
7862
send_ipython(publish[],

0 commit comments

Comments
 (0)