Skip to content

Commit

Permalink
RFC: move stringmime to Base64, rename reprmime -> repr (JuliaLang#25990
Browse files Browse the repository at this point in the history
)

* stringmime(text/plain, x) can be replaced with repr(text/plain, x)
  • Loading branch information
stevengj authored and JeffBezanson committed Feb 17, 2018
1 parent aed8814 commit 15a345b
Show file tree
Hide file tree
Showing 24 changed files with 144 additions and 99 deletions.
4 changes: 4 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,10 @@ Language changes
backslashes and the end of the literal while 2n+1 backslashes followed by a quote encodes n
backslashes followed by a quote character ([#22926]).

* `reprmime(mime, x)` has been renamed to `repr(mime, x)`, and along with `repr(x)`
and `sprint` it now accepts an optional `context` keyword for `IOContext` attributes.
`stringmime` has been moved to the Base64 stdlib package ([#25990]).

* The syntax `(x...)` for constructing a tuple is deprecated; use `(x...,)` instead ([#24452]).

* Non-parenthesized interpolated variables in strings, e.g. `"$x"`, must be followed
Expand Down
2 changes: 2 additions & 0 deletions base/deprecated.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1365,6 +1365,8 @@ end
@deprecate IOBuffer(read::Bool, write::Bool) IOBuffer(read=read, write=write)
@deprecate IOBuffer(maxsize::Integer) IOBuffer(read=true, write=true, maxsize=maxsize)

@deprecate reprmime(mime, x) repr(mime, x)

# PR #23332
@deprecate ^(x, p::Integer) Base.power_by_squaring(x,p)

Expand Down
2 changes: 0 additions & 2 deletions base/exports.jl
Original file line number Diff line number Diff line change
Expand Up @@ -875,8 +875,6 @@ export
istextmime,
MIME,
@MIME_str,
reprmime,
stringmime,
mimewritable,
popdisplay,
pushdisplay,
Expand Down
2 changes: 1 addition & 1 deletion base/loading.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1116,7 +1116,7 @@ function create_expr_cache(input::String, output::String, concrete_deps::typeof(
begin
import Pkg
empty!(Base.LOAD_PATH)
append!(Base.LOAD_PATH, $(repr(LOAD_PATH, :module => nothing)))
append!(Base.LOAD_PATH, $(repr(LOAD_PATH, context=:module=>nothing)))
empty!(Base.DEPOT_PATH)
append!(Base.DEPOT_PATH, $(repr(DEPOT_PATH)))
empty!(Base.LOAD_CACHE_PATH)
Expand Down
64 changes: 30 additions & 34 deletions base/multimedia.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
module Multimedia

export AbstractDisplay, display, pushdisplay, popdisplay, displayable, redisplay,
MIME, @MIME_str, reprmime, stringmime, istextmime,
MIME, @MIME_str, istextmime,
mimewritable, TextDisplay

###########################################################################
Expand All @@ -15,8 +15,7 @@ export AbstractDisplay, display, pushdisplay, popdisplay, displayable, redisplay
# struct MIME{mime} end
# macro MIME_str(s)
import Base: MIME, @MIME_str
import Base64
import Base: show, print, string, convert
import Base: show, print, string, convert, repr
MIME(s) = MIME{Symbol(s)}()
show(io::IO, ::MIME{mime}) where {mime} = print(io, "MIME type ", string(mime))
print(io::IO, ::MIME{mime}) where {mime} = print(io, mime)
Expand Down Expand Up @@ -79,59 +78,60 @@ show(stream, mime, x)
show(io::IO, m::AbstractString, x) = show(io, MIME(m), x)
mimewritable(m::AbstractString, x) = mimewritable(MIME(m), x)

verbose_show(io, m, x) = show(IOContext(io, :limit => false), m, x)

"""
reprmime(mime, x)
repr(mime, x; context=nothing)
Returns an `AbstractString` or `Vector{UInt8}` containing the representation of
`x` in the requested `mime` type, as written by [`show`](@ref) (throwing a
`x` in the requested `mime` type, as written by [`show(io, mime, x)`](@ref) (throwing a
[`MethodError`](@ref) if no appropriate `show` is available). An `AbstractString` is
returned for MIME types with textual representations (such as `"text/html"` or
`"application/postscript"`), whereas binary data is returned as
`Vector{UInt8}`. (The function `istextmime(mime)` returns whether or not Julia
treats a given `mime` type as text.)
The optional keyword argument `context` can be set to `:key=>value` pair
or an `IO` or [`IOContext`](@ref) object whose attributes are used for the I/O
stream passed to `show`.
As a special case, if `x` is an `AbstractString` (for textual MIME types) or a
`Vector{UInt8}` (for binary MIME types), the `reprmime` function assumes that
`Vector{UInt8}` (for binary MIME types), the `repr` function assumes that
`x` is already in the requested `mime` format and simply returns `x`. This
special case does not apply to the `"text/plain"` MIME type. This is useful so
that raw data can be passed to `display(m::MIME, x)`.
In particular, `repr("text/plain", x)` is typically a "pretty-printed" version
of `x` designed for human consumption. See also [`repr(x)`](@ref) to instead
return a string corresponding to [`show(x)`](@ref) that may be closer to how
the value of `x` would be entered in Julia.
# Examples
```jldoctest
julia> A = [1 2; 3 4];
julia> reprmime("text/plain", A)
julia> repr("text/plain", A)
"2×2 Array{Int64,2}:\\n 1 2\\n 3 4"
```
"""
reprmime(m::MIME, x) = istextmime(m) ? _textreprmime(m, x) : _binreprmime(m, x)
repr(m::MIME, x; context=nothing) = istextmime(m) ? _textrepr(m, x, context) : _binrepr(m, x, context)
repr(m::AbstractString, x; context=nothing) = repr(MIME(m), x; context=context)

# strings are shown escaped for text/plain
_textreprmime(m::MIME, x) = sprint(verbose_show, m, x)
_textreprmime(::MIME, x::AbstractString) = x
_textreprmime(m::MIME"text/plain", x::AbstractString) =
sprint(verbose_show, m, x)
_textrepr(m::MIME, x, context) = String(__binrepr(m, x, context))
_textrepr(::MIME, x::AbstractString, context) = x
_textrepr(m::MIME"text/plain", x::AbstractString, context) = String(__binrepr(m, x, context))

function _binreprmime(m::MIME, x)

function __binrepr(m::MIME, x, context)
s = IOBuffer()
verbose_show(s, m, x)
if context === nothing
show(s, m, x)
else
show(IOContext(s, context), m, x)
end
take!(s)
end
_binreprmime(m::MIME, x::Vector{UInt8}) = x

"""
stringmime(mime, x)
Returns an `AbstractString` containing the representation of `x` in the
requested `mime` type. This is similar to [`reprmime`](@ref) except
that binary data is base64-encoded as an ASCII string.
"""
stringmime(m::MIME, x) = istextmime(m) ? reprmime(m, x) : _binstringmime(m, x)

_binstringmime(m::MIME, x) = Base64.base64encode(verbose_show, m, x)
_binstringmime(m::MIME, x::Vector{UInt8}) = Base64.base64encode(write, x)
_binrepr(m::MIME, x, context) = __binrepr(m, x, context)
_binrepr(m::MIME, x::Vector{UInt8}, context) = x

"""
istextmime(m::MIME)
Expand All @@ -149,11 +149,7 @@ false
```
"""
istextmime(m::MIME) = startswith(string(m), "text/")

# it is convenient to accept strings instead of ::MIME
istextmime(m::AbstractString) = istextmime(MIME(m))
reprmime(m::AbstractString, x) = reprmime(MIME(m), x)
stringmime(m::AbstractString, x) = stringmime(MIME(m), x)

for mime in ["application/atom+xml", "application/ecmascript",
"application/javascript", "application/julia",
Expand All @@ -169,7 +165,7 @@ end
# We have an abstract AbstractDisplay class that can be subclassed in order to
# define new rich-display output devices. A typical subclass should
# overload display(d::AbstractDisplay, m::MIME, x) for supported MIME types m,
# (typically using reprmime or stringmime to get the MIME
# (typically using show, repr, ..., to get the MIME
# representation of x) and should also overload display(d::AbstractDisplay, x)
# to display x in whatever MIME type is preferred by the AbstractDisplay and
# is writable by x. display(..., x) should throw a MethodError if x
Expand Down
32 changes: 16 additions & 16 deletions base/strings/io.jl
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,16 @@ println(io::IO, xs...) = print(io, xs..., '\n')
## conversion of general objects to strings ##

"""
sprint(f::Function, args...)
sprint(f::Function, args...; context=nothing, sizehint=0)
Call the given function with an I/O stream and the supplied extra arguments.
Everything written to this I/O stream is returned as a string.
The optional keyword argument `context` can be set to `:key=>value` pair
or an `IO` or [`IOContext`](@ref) object whose attributes are used for the I/O
stream passed to `f`. The optional `sizehint` is a suggersted (in bytes)
to allocate for the buffer used to write the string.
# Examples
```jldoctest
julia> sprint(showcompact, 66.66666)
Expand Down Expand Up @@ -147,12 +152,17 @@ function print_quoted_literal(io, s::AbstractString)
end

"""
repr(x)
repr(x, context::Pair{Symbol,<:Any}...)
repr(x; context=nothing)
Create a string from any value using the [`show`](@ref) function.
If context pairs are given, the IO buffer used to capture `show` output
is wrapped in an `IOContext` object with those context pairs.
The optional keyword argument `context` can be set to an `IO` or [`IOContext`](@ref)
object whose attributes are used for the I/O stream passed to `show`.
Note that `repr(x)` is usually similar to how the value of `x` would
be entered in Julia. See also [`repr("text/plain", x)`](@ref) to instead
return a "pretty-printed" version of `x` designed more for human consumption,
equivalent to the REPL display of `x`.
# Examples
```jldoctest
Expand All @@ -164,17 +174,7 @@ julia> repr(zeros(3))
```
"""
function repr(x)
s = IOBuffer()
show(s, x)
String(take!(s))
end

function repr(x, context::Pair{Symbol}...)
s = IOBuffer()
show(IOContext(s, context...), x)
String(take!(s))
end
repr(x; context=nothing) = sprint(show, x; context=context)

# IOBuffer views of a (byte)string:

Expand Down
7 changes: 1 addition & 6 deletions base/sysimg.jl
Original file line number Diff line number Diff line change
Expand Up @@ -448,12 +448,6 @@ let BINDIR = ccall(:jl_get_julia_bindir, Any, ())
init_load_path(BINDIR)
end

INCLUDE_STATE = 3 # include = include_relative

import Base64

INCLUDE_STATE = 2

include("asyncmap.jl")

include("multimedia.jl")
Expand Down Expand Up @@ -576,6 +570,7 @@ Base.require(Base, :UUIDs)
@deprecate_stdlib base64decode Base64 true
@deprecate_stdlib Base64EncodePipe Base64 true
@deprecate_stdlib Base64DecodePipe Base64 true
@deprecate_stdlib stringmime Base64 true

@deprecate_stdlib poll_fd FileWatching true
@deprecate_stdlib poll_file FileWatching true
Expand Down
8 changes: 4 additions & 4 deletions doc/src/base/io-network.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,7 @@ Base.Multimedia.redisplay
Base.Multimedia.displayable
Base.show(::Any, ::Any, ::Any)
Base.Multimedia.mimewritable
Base.Multimedia.reprmime
Base.Multimedia.stringmime
Base.repr(::Any, ::Any)
```

As mentioned above, one can also define new display backends. For example, a module that can display
Expand All @@ -105,8 +104,9 @@ types with PNG representations will automatically display the image using the mo
In order to define a new display backend, one should first create a subtype `D` of the abstract
class `AbstractDisplay`. Then, for each MIME type (`mime` string) that can be displayed on `D`, one should
define a function `display(d::D, ::MIME"mime", x) = ...` that displays `x` as that MIME type,
usually by calling [`reprmime(mime, x)`](@ref). A `MethodError` should be thrown if `x` cannot be displayed
as that MIME type; this is automatic if one calls [`reprmime`](@ref). Finally, one should define a function
usually by calling [`show(io, mime, x)`](@ref) or [`repr(io, mime, x)`](@ref).
A `MethodError` should be thrown if `x` cannot be displayed
as that MIME type; this is automatic if one calls `show` or `repr`. Finally, one should define a function
`display(d::D, x)` that queries [`mimewritable(mime, x)`](@ref) for the `mime` types supported by `D`
and displays the "best" one; a `MethodError` should be thrown if no supported MIME types are found
for `x`. Similarly, some subtypes may wish to override [`redisplay(d::D, ...)`](@ref Base.Multimedia.redisplay). (Again, one should
Expand Down
2 changes: 1 addition & 1 deletion doc/src/base/strings.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ Base.:^(::AbstractString, ::Integer)
Base.string
Base.repeat(::AbstractString, ::Integer)
Base.repeat(::Char, ::Integer)
Base.repr
Base.repr(::Any)
Core.String(::AbstractString)
Base.SubString
Base.transcode
Expand Down
1 change: 1 addition & 0 deletions stdlib/Base64/docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ Base64.Base64EncodePipe
Base64.base64encode
Base64.Base64DecodePipe
Base64.base64decode
Base64.stringmime
```
20 changes: 19 additions & 1 deletion stdlib/Base64/src/Base64.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ export
Base64EncodePipe,
base64encode,
Base64DecodePipe,
base64decode
base64decode,
stringmime

# Base64EncodePipe is a pipe-like IO object, which converts into base64 data
# sent to a stream. (You must close the pipe to complete the encode, separate
Expand All @@ -23,4 +24,21 @@ include("buffer.jl")
include("encode.jl")
include("decode.jl")

"""
stringmime(mime, x; context=nothing)
Returns an `AbstractString` containing the representation of `x` in the
requested `mime` type. This is similar to [`repr(mime, x)`](@ref) except
that binary data is base64-encoded as an ASCII string.
The optional keyword argument `context` can be set to `:key=>value` pair
or an `IO` or [`IOContext`](@ref) object whose attributes are used for the I/O
stream passed to `show`.
"""
stringmime(m::MIME, x; context=nothing) = istextmime(m) ? Base.Multimedia._textrepr(m, x, context) : _binstringmime(m, x, context)
stringmime(m::AbstractString, x; context=nothing) = stringmime(MIME(m), x; context=context)

_binstringmime(m::MIME, x, context) = Base64.base64encode(show, m, x; context=IOContext)
_binstringmime(m::MIME, x::Vector{UInt8}, context) = Base64.base64encode(write, x; context=context)

end
18 changes: 13 additions & 5 deletions stdlib/Base64/src/encode.jl
Original file line number Diff line number Diff line change
Expand Up @@ -183,8 +183,8 @@ function loadtriplet!(buffer::Buffer, ptr::Ptr{UInt8}, n::UInt)
end

"""
base64encode(writefunc, args...)
base64encode(args...)
base64encode(writefunc, args...; context=nothing)
base64encode(args...; context=nothing)
Given a [`write`](@ref)-like function `writefunc`, which takes an I/O stream as
its first argument, `base64encode(writefunc, args...)` calls `writefunc` to
Expand All @@ -193,13 +193,21 @@ write `args...` to a base64-encoded string, and returns the string.
converts its arguments into bytes using the standard [`write`](@ref) functions
and returns the base64-encoded string.
The optional keyword argument `context` can be set to `:key=>value` pair
or an `IO` or [`IOContext`](@ref) object whose attributes are used for the I/O
stream passed to `writefunc` or `write`.
See also [`base64decode`](@ref).
"""
function base64encode(f::Function, args...)
function base64encode(f::Function, args...; context=nothing)
s = IOBuffer()
b = Base64EncodePipe(s)
f(b, args...)
if context === nothing
f(b, args...)
else
f(IOContext(b, context), args...)
end
close(b)
return String(take!(s))
end
base64encode(args...) = base64encode(write, args...)
base64encode(args...; context=nothing) = base64encode(write, args...; context=context)
11 changes: 10 additions & 1 deletion stdlib/Base64/test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import Base64:
Base64EncodePipe,
base64encode,
Base64DecodePipe,
base64decode
base64decode,
stringmime

const inputText = "Man is distinguished, not only by his reason, but by this singular passion from other animals, which is a lust of the mind, that by a perseverance of delight in the continued and indefatigable generation of knowledge, exceeds the short vehemence of any carnal pleasure."
const encodedMaxLine76 = """
Expand Down Expand Up @@ -75,3 +76,11 @@ end
@test hash(base64decode(base64encode(data))) == hash(data)
end
end

@testset "stringmime" begin
@test stringmime("text/plain", [1 2;3 4]) == repr("text/plain", [1 2;3 4])
@test stringmime("text/html", "raw html data") == "raw html data"
@test stringmime("text/plain", "string") == "\"string\""
@test stringmime("image/png", UInt8[2,3,4,7]) == "AgMEBw=="
@test stringmime("text/plain", 3.141592653589793, context=:compact=>true) == "3.14159"
end
2 changes: 2 additions & 0 deletions stdlib/Markdown/Project.toml
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
name = "Markdown"
uuid = "d6f4376e-aef5-505a-96c1-9c027394607a"

[deps]
Base64 = "2a0f44e3-6c83-55bd-87e4-b1978d98bd5f"
1 change: 1 addition & 0 deletions stdlib/Markdown/src/Markdown.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ Tools for working with the Markdown file format. Mainly for documentation.
module Markdown

import Base: show, ==, with_output_color
using Base64: stringmime

include(joinpath("parse", "config.jl"))
include(joinpath("parse", "util.jl"))
Expand Down
2 changes: 1 addition & 1 deletion stdlib/Pkg/src/entry.jl
Original file line number Diff line number Diff line change
Expand Up @@ -585,7 +585,7 @@ function build(pkg::AbstractString, build_file::AbstractString, errfile::Abstrac
code = """
import Pkg
empty!(Base.LOAD_PATH)
append!(Base.LOAD_PATH, $(repr(LOAD_PATH, :module => nothing)))
append!(Base.LOAD_PATH, $(repr(LOAD_PATH, context=:module=>nothing)))
empty!(Base.DEPOT_PATH)
append!(Base.DEPOT_PATH, $(repr(DEPOT_PATH)))
empty!(Base.LOAD_CACHE_PATH)
Expand Down
Loading

0 comments on commit 15a345b

Please sign in to comment.