diff --git a/base/errorshow.jl b/base/errorshow.jl index 3666c7daf7080..0433cedc164c0 100644 --- a/base/errorshow.jl +++ b/base/errorshow.jl @@ -278,7 +278,8 @@ function showerror(io::IO, ex::MethodError) f_is_function = true end print(io, "no method matching ") - iob = IOContext(IOBuffer(), io) # for type abbreviation as in #49795; some, like `convert(T, x)`, should not abbreviate + buf = IOBuffer() + iob = IOContext(buf, io) # for type abbreviation as in #49795; some, like `convert(T, x)`, should not abbreviate show_signature_function(iob, isa(f, Type) ? Type{f} : typeof(f)) print(iob, "(") for (i, typ) in enumerate(arg_types_param) @@ -293,7 +294,7 @@ function showerror(io::IO, ex::MethodError) end end print(iob, ")") - str = String(take!(unwrapcontext(iob)[1])) + str = String(take!(buf)) str = type_limited_string_from_context(io, str) print(io, str) end diff --git a/base/io.jl b/base/io.jl index fd286297ec090..7d93bc3e6d9c8 100644 --- a/base/io.jl +++ b/base/io.jl @@ -414,7 +414,14 @@ end """ AbstractPipe -`AbstractPipe` is the abstract supertype for IO pipes that provide for communication between processes. +`AbstractPipe` is an abstract supertype that exists for the convenience of creating +pass-through wrappers for other IO objects, so that you only need to implement the +additional methods relevant to your type. A subtype only needs to implement one or both of +these methods: + + struct P <: AbstractPipe; ...; end + pipe_reader(io::P) = io.out + pipe_writer(io::P) = io.in If `pipe isa AbstractPipe`, it must obey the following interface: diff --git a/base/show.jl b/base/show.jl index db0b429f3386f..c74e2789e26ed 100644 --- a/base/show.jl +++ b/base/show.jl @@ -294,27 +294,35 @@ struct IOContext{IO_t <: IO} <: AbstractPipe dict::ImmutableDict{Symbol, Any} function IOContext{IO_t}(io::IO_t, dict::ImmutableDict{Symbol, Any}) where IO_t<:IO - @assert !(IO_t <: IOContext) "Cannot create `IOContext` from another `IOContext`." + io isa IOContext && (io = io.io) # implicitly unwrap, since the io.dict field is not useful anymore, and could confuse pipe_reader consumers return new(io, dict) end end -# (Note that TTY and TTYTerminal io types have a :color property.) -unwrapcontext(io::IO) = io, get(io,:color,false) ? ImmutableDict{Symbol,Any}(:color, true) : ImmutableDict{Symbol,Any}() -unwrapcontext(io::IOContext) = io.io, io.dict +# (Note that TTY and TTYTerminal io types have an implied :color property.) +ioproperties(io::IO) = get(io, :color, false) ? ImmutableDict{Symbol,Any}(:color, true) : ImmutableDict{Symbol,Any}() +ioproperties(io::IOContext) = io.dict +# these can probably be deprecated, but there is a use in the ecosystem for them +unwrapcontext(io::IO) = (io,) +unwrapcontext(io::IOContext) = (io.io,) -function IOContext(io::IO, dict::ImmutableDict) - io0 = unwrapcontext(io)[1] - IOContext{typeof(io0)}(io0, dict) +function IOContext(io::IO, dict::ImmutableDict{Symbol, Any}) + return IOContext{typeof(io)}(io, dict) end -convert(::Type{IOContext}, io::IO) = IOContext(unwrapcontext(io)...)::IOContext +function IOContext(io::IOContext, dict::ImmutableDict{Symbol, Any}) + return typeof(io)(io.io, dict) +end + + +convert(::Type{IOContext}, io::IOContext) = io +convert(::Type{IOContext}, io::IO) = IOContext(io, ioproperties(io))::IOContext IOContext(io::IO) = convert(IOContext, io) function IOContext(io::IO, KV::Pair) - io0, d = unwrapcontext(io) - IOContext(io0, ImmutableDict{Symbol,Any}(d, KV[1], KV[2])) + d = ioproperties(io) + return IOContext(io, ImmutableDict{Symbol,Any}(d, KV[1], KV[2])) end """ @@ -322,7 +330,7 @@ end Create an `IOContext` that wraps an alternate `IO` but inherits the properties of `context`. """ -IOContext(io::IO, context::IO) = IOContext(unwrapcontext(io)[1], unwrapcontext(context)[2]) +IOContext(io::IO, context::IO) = IOContext(io, ioproperties(context)) """ IOContext(io::IO, KV::Pair...) @@ -2548,7 +2556,8 @@ function show_tuple_as_call(out::IO, name::Symbol, sig::Type; return end tv = Any[] - io = IOContext(IOBuffer(), out) + buf = IOBuffer() + io = IOContext(buf, out) env_io = io while isa(sig, UnionAll) push!(tv, sig.var) @@ -2591,7 +2600,7 @@ function show_tuple_as_call(out::IO, name::Symbol, sig::Type; end print_within_stacktrace(io, ")", bold=true) show_method_params(io, tv) - str = String(take!(unwrapcontext(io)[1])) + str = String(take!(buf)) str = type_limited_string_from_context(out, str) print(out, str) nothing diff --git a/base/stream.jl b/base/stream.jl index 3124b8b0c0a24..542e77ec81cdd 100644 --- a/base/stream.jl +++ b/base/stream.jl @@ -743,19 +743,28 @@ mutable struct Pipe <: AbstractPipe end """ -Construct an uninitialized Pipe object. + Pipe() -The appropriate end of the pipe will be automatically initialized if -the object is used in process spawning. This can be useful to easily -obtain references in process pipelines, e.g.: +Construct an uninitialized Pipe object, especially for IO communication between multiple processes. + +The appropriate end of the pipe will be automatically initialized if the object is used in +process spawning. This can be useful to easily obtain references in process pipelines, e.g.: ``` julia> err = Pipe() # After this `err` will be initialized and you may read `foo`'s -# stderr from the `err` pipe. +# stderr from the `err` pipe, or pass `err` to other pipelines. julia> run(pipeline(pipeline(`foo`, stderr=err), `cat`), wait=false) + +# Now destroy the write half of the pipe, so that the read half will get EOF +julia> closewrite(err) + +julia> read(err, String) +"stderr messages" ``` + +See also `Base.link_pipe!`. """ Pipe() = Pipe(PipeEndpoint(), PipeEndpoint()) pipe_reader(p::Pipe) = p.out