Skip to content

Commit 26e7eb2

Browse files
author
Christopher Doris
committed
add line buffering to pyio
1 parent 7ded9b1 commit 26e7eb2

File tree

3 files changed

+32
-10
lines changed

3 files changed

+32
-10
lines changed

Diff for: docs/src/releasenotes.md

+4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
# Release Notes
22

3+
## Unreleased
4+
* Adds `line_buffering` option to `PyIO`.
5+
* Improvements to stdout when using `juliacall.ipython` includng line-buffering.
6+
37
## 0.9.7 (2022-10-11)
48
* If CondaPkg is using the Null backend, PythonCall will now use `python` from the PATH.
59
* Bug fixes.

Diff for: pysrc/juliacall/ipython.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ def load_ipython_extension(ip):
3434
PythonCall.seval("""begin
3535
const _redirected_stdout = redirect_stdout()
3636
const _redirected_stderr = redirect_stderr()
37-
const _py_stdout = PyIO(pyimport("sys" => "stdout"))
38-
const _py_stderr = PyIO(pyimport("sys" => "stderr"))
37+
const _py_stdout = PyIO(pyimport("sys" => "stdout"); line_buffering=true)
38+
const _py_stderr = PyIO(pyimport("sys" => "stderr"); line_buffering=true)
3939
const _redirect_stdout_task = @async write($_py_stdout, $_redirected_stdout)
4040
const _redirect_stderr_task = @async write($_py_stderr, $_redirected_stderr)
4141
function _flush_stdio()

Diff for: src/pywrap/PyIO.jl

+26-8
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
"""
2-
PyIO(x; own=false, text=missing, buflen=4096)
2+
PyIO(x; own=false, text=missing, line_buffering=false, buflen=4096)
33
44
Wrap the Python IO stream `x` as a Julia IO stream.
55
@@ -10,6 +10,8 @@ If `text=true` then `x` must be a text stream and only UTF-8 must be written (i.
1010
If `text` is not specified then it is chosen automatically.
1111
If `x` is a text stream and you really need a binary stream, then often `PyIO(x.buffer)` will work.
1212
13+
If `line_buffering=true` then output is flushed at each line.
14+
1315
For efficiency, reads and writes are buffered before being sent to `x`.
1416
The size of the buffers is `buflen`.
1517
The buffers are cleared using `flush`.
@@ -20,6 +22,8 @@ mutable struct PyIO <: IO
2022
own::Bool
2123
# true if `o` is text, false if binary
2224
text::Bool
25+
# true to flush whenever '\n' or '\r' is encountered
26+
line_buffering::Bool
2327
# true if we are definitely at the end of the file; false if we are not or don't know
2428
eof::Bool
2529
# input buffer
@@ -29,7 +33,7 @@ mutable struct PyIO <: IO
2933
obuflen::Int
3034
obuf::Vector{UInt8}
3135

32-
function PyIO(x; own::Bool = false, text::Union{Missing,Bool} = missing, buflen::Integer = 4096, ibuflen::Integer = buflen, obuflen::Integer = buflen)
36+
function PyIO(x; own::Bool = false, text::Union{Missing,Bool} = missing, buflen::Integer = 4096, ibuflen::Integer = buflen, obuflen::Integer = buflen, line_buffering::Bool=false)
3337
if text === missing
3438
text = pyhasattr(x, "encoding")
3539
end
@@ -39,7 +43,7 @@ mutable struct PyIO <: IO
3943
ibuflen > 0 || error("ibuflen must be positive")
4044
obuflen = convert(Int, obuflen)
4145
obuflen > 0 || error("obuflen must be positive")
42-
new(Py(x), own, text, false, ibuflen, UInt8[], obuflen, UInt8[])
46+
new(Py(x), own, text, line_buffering, false, ibuflen, UInt8[], obuflen, UInt8[])
4347
end
4448
end
4549
export PyIO
@@ -136,23 +140,37 @@ Base.isopen(io::PyIO) = !pyconvert(Bool, @py io.closed)
136140

137141
function Base.unsafe_write(io::PyIO, ptr::Ptr{UInt8}, n::UInt)
138142
ntodo = n
139-
while true
143+
while ntodo > 0
140144
nroom = max(0, io.obuflen - length(io.obuf))
141145
if ntodo < nroom
142-
append!(io.obuf, unsafe_wrap(Array, ptr, ntodo))
143-
return n
146+
buf = unsafe_wrap(Array, ptr, ntodo)
147+
if io.line_buffering
148+
i = findlast(((0x0A, 0x0D)), buf)
149+
if i === nothing
150+
append!(io.obuf, buf)
151+
else
152+
append!(io.obuf, unsafe_wrap(Array, ptr, i))
153+
putobuf(io)
154+
append!(io.obuf, unsafe_wrap(Array, ptr+i, ntodo-i))
155+
end
156+
else
157+
append!(io.obuf, buf)
158+
end
159+
break
144160
else
145-
append!(io.obuf, unsafe_wrap(Array, ptr, nroom))
161+
buf = unsafe_wrap(Array, ptr, nroom)
162+
append!(io.obuf, buf)
146163
putobuf(io)
147164
ptr += nroom
148165
ntodo -= nroom
149166
end
150167
end
168+
return n
151169
end
152170

153171
function Base.write(io::PyIO, c::UInt8)
154172
push!(io.obuf, c)
155-
if length(io.obuf) io.obuflen
173+
if (length(io.obuf) io.obuflen) || (io.line_buffering && (c == 0x0A || c == 0x0D))
156174
putobuf(io)
157175
end
158176
return

0 commit comments

Comments
 (0)