Skip to content

Commit 986b8b7

Browse files
authored
improve redirect_std* functions (#39132)
Handles Pipe now as an argument (as documented), and reduces use of metaprogramming by introducing a name for the common functionality. Fixes #13519
1 parent d69b9b7 commit 986b8b7

File tree

1 file changed

+84
-79
lines changed

1 file changed

+84
-79
lines changed

base/stream.jl

Lines changed: 84 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -1127,105 +1127,119 @@ function _fd(x::Union{LibuvStream, LibuvServer})
11271127
return fd[]
11281128
end
11291129

1130-
for (x, writable, unix_fd, c_symbol) in
1131-
((:stdin, false, 0, :jl_uv_stdin),
1132-
(:stdout, true, 1, :jl_uv_stdout),
1133-
(:stderr, true, 2, :jl_uv_stderr))
1134-
f = Symbol("redirect_", lowercase(string(x)))
1135-
_f = Symbol("_", f)
1136-
@eval begin
1137-
function ($_f)(stream)
1138-
global $x
1139-
posix_fd = _fd(stream)
1140-
@static if Sys.iswindows()
1141-
ccall(:SetStdHandle, stdcall, Int32, (Int32, OS_HANDLE),
1142-
$(-10 - unix_fd), Libc._get_osfhandle(posix_fd))
1143-
end
1144-
dup(posix_fd, RawFD($unix_fd))
1145-
$x = stream
1146-
nothing
1147-
end
1148-
function ($f)(handle::Union{LibuvStream, IOStream})
1149-
$(_f)(handle)
1150-
unsafe_store!(cglobal($(Expr(:quote, c_symbol)), Ptr{Cvoid}),
1151-
handle.handle)
1152-
return handle
1153-
end
1154-
function ($f)()
1155-
p = link_pipe!(Pipe())
1156-
read, write = p.out, p.in
1157-
($f)($(writable ? :write : :read))
1158-
return (read, write)
1159-
end
1160-
function ($f)(::DevNull)
1161-
global $x
1162-
nulldev = @static Sys.iswindows() ? "NUL" : "/dev/null"
1163-
handle = open(nulldev, write=$writable)
1164-
$(_f)(handle)
1165-
close(handle) # handle has been dup'ed in $(_f)
1166-
$x = devnull
1167-
return devnull
1168-
end
1169-
function ($f)(io::IOContext)
1170-
io2, _dict = unwrapcontext(io)
1171-
($f)(io2)
1172-
global $x = io
1173-
return io
1130+
struct redirect_stdio <: Function
1131+
unix_fd::Int
1132+
writable::Bool
1133+
end
1134+
for (f, writable, unix_fd) in
1135+
((:redirect_stdin, false, 0),
1136+
(:redirect_stdout, true, 1),
1137+
(:redirect_stderr, true, 2))
1138+
@eval const ($f) = redirect_stdio($unix_fd, $writable)
1139+
end
1140+
function _redirect_io_libc(stream, unix_fd::Int)
1141+
posix_fd = _fd(stream)
1142+
@static if Sys.iswindows()
1143+
if 0 <= unix_fd <= 2
1144+
ccall(:SetStdHandle, stdcall, Int32, (Int32, OS_HANDLE),
1145+
-10 - unix_fd, Libc._get_osfhandle(posix_fd))
11741146
end
11751147
end
1148+
dup(posix_fd, RawFD(unix_fd))
1149+
nothing
1150+
end
1151+
function _redirect_io_global(io, unix_fd::Int)
1152+
unix_fd == 0 && (global stdin = io)
1153+
unix_fd == 1 && (global stdout = io)
1154+
unix_fd == 2 && (global stderr = io)
1155+
nothing
1156+
end
1157+
function (f::redirect_stdio)(handle::Union{LibuvStream, IOStream})
1158+
_redirect_io_libc(handle, f.unix_fd)
1159+
c_sym = f.unix_fd == 0 ? cglobal(:jl_uv_stdin, Ptr{Cvoid}) :
1160+
f.unix_fd == 1 ? cglobal(:jl_uv_stdout, Ptr{Cvoid}) :
1161+
f.unix_fd == 2 ? cglobal(:jl_uv_stderr, Ptr{Cvoid}) :
1162+
C_NULL
1163+
c_sym == C_NULL || unsafe_store!(c_sym, handle.handle)
1164+
_redirect_io_global(handle, f.unix_fd)
1165+
return handle
1166+
end
1167+
function (f::redirect_stdio)(::DevNull)
1168+
nulldev = @static Sys.iswindows() ? "NUL" : "/dev/null"
1169+
handle = open(nulldev, write=f.writable)
1170+
_redirect_io_libc(handle, f.unix_fd)
1171+
close(handle) # handle has been dup'ed in _redirect_io_libc
1172+
_redirect_io_global(devnull, f.unix_fd)
1173+
return devnull
1174+
end
1175+
function (f::redirect_stdio)(io::AbstractPipe)
1176+
io2 = (f.writable ? pipe_writer : pipe_reader)(io)
1177+
f(io2)
1178+
_redirect_io_global(io, f.unix_fd)
1179+
return io
1180+
end
1181+
function (f::redirect_stdio)(p::Pipe)
1182+
if p.in.status == StatusInit && p.out.status == StatusInit
1183+
link_pipe!(p)
1184+
end
1185+
io2 = getfield(p, f.writable ? :in : :out)
1186+
f(io2)
1187+
return p
11761188
end
1189+
(f::redirect_stdio)() = f(Pipe())
1190+
1191+
# Deprecate these in v2 (redirect_stdio support)
1192+
iterate(p::Pipe) = (p.out, 1)
1193+
iterate(p::Pipe, i::Int) = i == 1 ? (p.in, 2) : nothing
1194+
getindex(p::Pipe, key::Int) = key == 1 ? p.out : key == 2 ? p.in : throw(KeyError(key))
11771195

11781196
"""
1179-
redirect_stdout([stream]) -> (rd, wr)
1197+
redirect_stdout([stream]) -> stream
11801198
11811199
Create a pipe to which all C and Julia level [`stdout`](@ref) output
1182-
will be redirected.
1183-
Returns a tuple `(rd, wr)` representing the pipe ends.
1200+
will be redirected. Return a stream representing the pipe ends.
11841201
Data written to [`stdout`](@ref) may now be read from the `rd` end of
1185-
the pipe. The `wr` end is given for convenience in case the old
1186-
[`stdout`](@ref) object was cached by the user and needs to be replaced
1187-
elsewhere.
1188-
1189-
If called with the optional `stream` argument, then returns `stream` itself.
1202+
the pipe.
11901203
11911204
!!! note
1192-
`stream` must be an `IOStream`, a `TTY`, a `Pipe`, a socket, or `devnull`.
1205+
`stream` must be a compatible objects, such as an `IOStream`, `TTY`,
1206+
`Pipe`, socket, or `devnull`.
11931207
"""
11941208
redirect_stdout
11951209

11961210
"""
1197-
redirect_stderr([stream]) -> (rd, wr)
1211+
redirect_stderr([stream]) -> stream
11981212
11991213
Like [`redirect_stdout`](@ref), but for [`stderr`](@ref).
12001214
12011215
!!! note
1202-
`stream` must be an `IOStream`, a `TTY`, a `Pipe`, a socket, or `devnull`.
1216+
`stream` must be a compatible objects, such as an `IOStream`, `TTY`,
1217+
`Pipe`, socket, or `devnull`.
12031218
"""
12041219
redirect_stderr
12051220

12061221
"""
1207-
redirect_stdin([stream]) -> (rd, wr)
1222+
redirect_stdin([stream]) -> stream
12081223
12091224
Like [`redirect_stdout`](@ref), but for [`stdin`](@ref).
1210-
Note that the order of the return tuple is still `(rd, wr)`,
1211-
i.e. data to be read from [`stdin`](@ref) may be written to `wr`.
1225+
Note that the direction of the stream is reversed.
12121226
12131227
!!! note
1214-
`stream` must be an `IOStream`, a `TTY`, a `Pipe`, a socket, or `devnull`.
1228+
`stream` must be a compatible objects, such as an `IOStream`, `TTY`,
1229+
`Pipe`, socket, or `devnull`.
12151230
"""
12161231
redirect_stdin
12171232

1218-
for (F,S) in ((:redirect_stdin, :stdin), (:redirect_stdout, :stdout), (:redirect_stderr, :stderr))
1219-
@eval function $F(f::Function, stream)
1220-
STDOLD = $S
1221-
local ret
1222-
$F(stream)
1223-
try
1224-
ret = f()
1225-
finally
1226-
$F(STDOLD)
1227-
end
1228-
ret
1233+
function (f::redirect_stdio)(thunk::Function, stream)
1234+
stdold = f.unix_fd == 0 ? stdin :
1235+
f.unix_fd == 1 ? stdout :
1236+
f.unix_fd == 2 ? stderr :
1237+
throw(ArgumentError("Not implemented to get old handle of fd except for stdio"))
1238+
f(stream)
1239+
try
1240+
return thunk()
1241+
finally
1242+
f(stdold)
12291243
end
12301244
end
12311245

@@ -1234,9 +1248,6 @@ end
12341248
12351249
Run the function `f` while redirecting [`stdout`](@ref) to `stream`.
12361250
Upon completion, [`stdout`](@ref) is restored to its prior setting.
1237-
1238-
!!! note
1239-
`stream` must be a `TTY`, a `Pipe`, or a socket.
12401251
"""
12411252
redirect_stdout(f::Function, stream)
12421253

@@ -1245,9 +1256,6 @@ redirect_stdout(f::Function, stream)
12451256
12461257
Run the function `f` while redirecting [`stderr`](@ref) to `stream`.
12471258
Upon completion, [`stderr`](@ref) is restored to its prior setting.
1248-
1249-
!!! note
1250-
`stream` must be a `TTY`, a `Pipe`, or a socket.
12511259
"""
12521260
redirect_stderr(f::Function, stream)
12531261

@@ -1256,9 +1264,6 @@ redirect_stderr(f::Function, stream)
12561264
12571265
Run the function `f` while redirecting [`stdin`](@ref) to `stream`.
12581266
Upon completion, [`stdin`](@ref) is restored to its prior setting.
1259-
1260-
!!! note
1261-
`stream` must be a `TTY`, a `Pipe`, or a socket.
12621267
"""
12631268
redirect_stdin(f::Function, stream)
12641269

0 commit comments

Comments
 (0)