-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
need a way to redirect STDOUT/ERR/IN to Julia streams #3823
Comments
Yeah, I have an idea. Hold on. |
Hope you don't mind using lots of unexported API: julia> read, write = Base.NamedPipe(C_NULL), Base.NamedPipe(C_NULL)
(NamedPipe(null, 0 bytes waiting),NamedPipe(null, 0 bytes waiting))
julia> Base.link_pipe(read,true,write,false)
julia> eval(Base,:(STDOUT = $write))
NamedPipe(open, 0 bytes waiting)
julia> println("Hello World")
julia> readline(read)
"Hello World\n" Note that you won't be able to test this with the default readline REPL, as that just writes to STDOUT. However, if you grab the latest master of my using REPL
using Terminals
if length(ARGS) == 1
include(ARGS[1])
else
REPL.run_repl(Terminals.Unix.UnixTerminal("xterm",STDIN,STDOUT,STDERR))
end you should be able to play with this more easily, since it grabs the original TTY streams. |
After that, we might also want to actually |
Also add the necessary c functions to extract the FD/Handle from a pipe object. These aren't used anywhere, but will be helpful for #3823
Ok, I have tested this on linux. Should work on windows as well: using REPL
using Terminals
@unix_only dup(x::RawFD) = RawFD(ccall(:dup,Int32,(Int32,),x.fd))
@windows_only dup(x::RawFD) = RawFD(ccall(:_dup,Int32,(Int32,),x.fd))
@unix_only dup(src::RawFD,target::RawFD) = systemerror("dup",ccall(:dup2,Int32,(Int32,Int32),src.fd,target.fd) == -1)
import Base: NamedPipe, fd, TTY
@unix_only fd(x::NamedPipe) = RawFD(ccall(:jl_uv_pipe_fd,Int32,(Ptr{Void},),x.handle))
@windows_only fd(x::NamedPipe) = WindowsRawSocket(ccall(:jl_uv_pipe_handle,Ptr{Void},(Ptr{Void},),x.handle))
old_in = dup(RawFD(0))
old_out = dup(RawFD(1))
old_err = dup(RawFD(2))
stdin_read, stdin_write = (Base.NamedPipe(C_NULL), Base.NamedPipe(C_NULL))
stdout_read, stdout_write = (Base.NamedPipe(C_NULL), Base.NamedPipe(C_NULL))
stderr_read, stderr_write = (Base.NamedPipe(C_NULL), Base.NamedPipe(C_NULL))
Base.link_pipe(stdin_read,false,stdin_write,true)
Base.link_pipe(stdout_read,true,stdout_write,false)
Base.link_pipe(stderr_read,true,stderr_write,false)
@unix_only begin
dup(fd(stdin_read), RawFD(0))
dup(fd(stdout_write), RawFD(1))
dup(fd(stderr_write), RawFD(2))
end
@windows_only begin
ccall(:SetStdHandle,stdcall,Int32,(Uint32,Ptr{Void}),-10,fd(stdin_read).handle)
ccall(:SetStdHandle,stdcall,Int32,(Uint32,Ptr{Void}),-11,fd(stdout_read).handle)
ccall(:SetStdHandle,stdcall,Int32,(Uint32,Ptr{Void}),-12,fd(stderr_read).handle)
end
eval(Base,quote
STDIN = $stdin_read
STDOUT = $stdout_write
STDERR = $stderr_write
end)
if length(ARGS) == 1
include(ARGS[1])
else
REPL.run_repl(Terminals.Unix.UnixTerminal("xterm",TTY(old_in;readable=true),TTY(old_out),TTY(old_err)))
end |
Great! @JeffBezanson and @StefanKarpinski, should we put something like this directly into Base or keep it in julia-ipython? |
By "like this", I mean a standard API for interposing a custom |
Awesome, thanks! Since this uses Can we have an API that looks more like: stdout_read, stdout_write = pipeends()
dup(stdout_write, STDOUT) |
No, we still need to assign STDOUT, since it's not a TTY anymore. |
Plus windows doesn't actually rely on the file descriptors, but on the handle that was assigned to init on startup. |
The TTY distinction continues to annoy I see. In this case, that libuv detail seems to entirely prevent us from having an API like |
Implemented as proposed by @JeffBezanson above: read, write = stdout_redirect() |
What do we do if we still want to write some things to the original stdout? (e.g. logging messages?) |
You'll have to dup the original file descriptors. I can add convenience methods that do this. |
Convenience methods to save IO streams for the original file descriptors would be great, thanks. |
@loladiro instead of un-reverting your hack, this should be rewritten as a unconditional dup of the file descriptors at startup (so the originals are never affected), and writen in C. There's no reason for |
|
It hacks into libuv in ways that are unnecessary and potentially unsafe |
I don't mind duping the file descriptor on startup. I was gonna do that anyway. That doesn't solve the issue of having the |
I do apologize for not pull-requesting this though. I probably should have done that. |
@stevengj No need to call any save functions now
before redirecting will do the trick. |
The point is, if you are doing the dup at startup, then it should be done in C, before creating the libuv objects. That means you never need to close or change the object libuv originally opened for stdout. It also should mean that you don't need to hack libuv to get the handles. Can you revert the jl_uv_pipe_fd stuff again? Or does link pipe not actually expose the handle? I was thinking it did. I like being able to comment on pull request before they affect master. It reduces the urgency of making fixes, and the number of patches. |
|
Julia
gives the output
In other words, |
Nevermind, looks like it is just the fact that libc is not flushing |
See also #3949, which is needed to easily flush C's |
I discovered something strange. When I run
The async task will not do anything unless I do HOWEVER, if I write something to STDOUT fist
then the last line will trigger the async task and print "STDOUT.....", even without flushing. |
I've often wished for something like this: out = IOBuffer()
err = IOBuffer()
open(cmd, "w", stdout=out, stderr=err) do stdin
...
end Would that be possible? |
@damiendr, that's really a separate issue. |
Stdlib: Pkg URL: https://github.com/JuliaLang/Pkg.jl.git Stdlib branch: master Julia branch: master Old commit: 48eea8dbd New commit: e7d740ac8 Julia version: 1.12.0-DEV Pkg version: 1.11.0(Does not match) Bump invoked by: @KristofferC Powered by: [BumpStdlibs.jl](https://github.com/JuliaLang/BumpStdlibs.jl) Diff: JuliaLang/Pkg.jl@48eea8d...e7d740a ``` $ git log --oneline 48eea8dbd..e7d740ac8 e7d740ac8 move to using Base parallel precompile (#3820) d1f91fd37 fix relative paths in test project for `[sources]` (#3825) 5c73d7f3c Support a `[sources]` section in Project.toml for specifying paths and repo locations for dependencies (#3783) 0d9aa51a9 do not use UnstableIO for subprocess (in e.g. Pkg.test) (#3823) ``` Co-authored-by: Dilum Aluthge <dilum@aluthge.com>
…#53610) Stdlib: Pkg URL: https://github.com/JuliaLang/Pkg.jl.git Stdlib branch: master Julia branch: master Old commit: 48eea8dbd New commit: e7d740ac8 Julia version: 1.12.0-DEV Pkg version: 1.11.0(Does not match) Bump invoked by: @KristofferC Powered by: [BumpStdlibs.jl](https://github.com/JuliaLang/BumpStdlibs.jl) Diff: JuliaLang/Pkg.jl@48eea8d...e7d740a ``` $ git log --oneline 48eea8dbd..e7d740ac8 e7d740ac8 move to using Base parallel precompile (JuliaLang#3820) d1f91fd37 fix relative paths in test project for `[sources]` (JuliaLang#3825) 5c73d7f3c Support a `[sources]` section in Project.toml for specifying paths and repo locations for dependencies (JuliaLang#3783) 0d9aa51a9 do not use UnstableIO for subprocess (in e.g. Pkg.test) (JuliaLang#3823) ``` Co-authored-by: Dilum Aluthge <dilum@aluthge.com>
In order to implement the IPython front-end (see e.g. julia-ipython issue #5), we need to be able to capture stdout and stderr and redirect them to messages periodically flushed to a ZMQ socket (to send to the IPython front-end). We also need to capture stdin read requests and turn them in to requests over ZMQ to the IPython front-end.
We can, of course, temporarily modify the variables
STDIN
etcetera (although even this seems to require some changes toBase
, since Julia doesn't like it when code tries to change globals owned by another module). But it would be much nicer to capture the actual file descriptors, e.g. so that we can capture output from external C libraries.@JeffBezanson suggested doing some sort of dup on the file descriptors (though this may be harder on Windows). @loladiro, any thoughts?
The text was updated successfully, but these errors were encountered: