Skip to content

Commit fcab47e

Browse files
committed
fix: guard PyDict REPL completion with GIL
1 parent beec1ec commit fcab47e

5 files changed

Lines changed: 35 additions & 5 deletions

File tree

src/Core/Core.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ const ROOT_DIR = dirname(dirname(@__DIR__))
1111
using ..PythonCall
1212
using ..C
1313
using ..GC: GC
14+
using ..GIL
1415
using ..Utils
1516

1617
using Base: @propagate_inbounds, @kwdef

src/Core/Py.jl

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -125,10 +125,12 @@ Py(x::Date) = pydate(x)
125125
Py(x::Time) = pytime(x)
126126
Py(x::DateTime) = pydatetime(x)
127127

128-
Base.string(x::Py) = pyisnull(x) ? "<py NULL>" : pystr(String, x)
128+
Base.string(x::Py) = GIL.@lock (pyisnull(x) ? "<py NULL>" : pystr(String, x))
129129
Base.print(io::IO, x::Py) = print(io, string(x))
130130

131-
function Base.show(io::IO, x::Py)
131+
Base.show(io::IO, x::Py) = GIL.@lock _show(io, x)
132+
133+
function _show(io::IO, x::Py)
132134
if get(io, :typeinfo, Any) == Py
133135
if pyisnull(x)
134136
print(io, "NULL")
@@ -149,7 +151,9 @@ function Base.show(io::IO, x::Py)
149151
end
150152
end
151153

152-
function Base.show(io::IO, ::MIME"text/plain", o::Py)
154+
Base.show(io::IO, mime::MIME"text/plain", o::Py) = GIL.@lock _show(io, mime, o)
155+
156+
function _show(io::IO, ::MIME"text/plain", o::Py)
153157
if pyisnull(o)
154158
str = "NULL"
155159
else

src/Wrap/PyDict.jl

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,13 @@ end
1515

1616
Base.length(x::PyDict) = Int(pylen(x))
1717

18-
function Base.iterate(x::PyDict{K,V}, it::Py = pyiter(x)) where {K,V}
18+
Base.iterate(x::PyDict) = GIL.@lock _iterate(x, pyiter(x))
19+
20+
function Base.iterate(x::PyDict{K,V}, it::Py) where {K,V}
21+
GIL.@lock _iterate(x, it)
22+
end
23+
24+
function _iterate(x::PyDict{K,V}, it::Py) where {K,V}
1925
k_ = unsafe_pynext(it)
2026
pyisnull(k_) && return nothing
2127
v_ = pygetitem(x, k_)
@@ -24,7 +30,13 @@ function Base.iterate(x::PyDict{K,V}, it::Py = pyiter(x)) where {K,V}
2430
return (k => v, it)
2531
end
2632

27-
function Base.iterate(x::Base.KeySet{K,PyDict{K,V}}, it::Py = pyiter(x.dict)) where {K,V}
33+
Base.iterate(x::Base.KeySet{K,<:PyDict{K}}) where {K} = GIL.@lock _iterate(x, pyiter(x.dict))
34+
35+
function Base.iterate(x::Base.KeySet{K,<:PyDict{K}}, it::Py) where {K}
36+
GIL.@lock _iterate(x, it)
37+
end
38+
39+
function _iterate(x::Base.KeySet{K,<:PyDict{K}}, it::Py) where {K}
2840
k_ = unsafe_pynext(it)
2941
pyisnull(k_) && return nothing
3042
k = pyconvert(K, k_)

src/Wrap/Wrap.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ using ..NumpyDates
1111
using ..C
1212
using ..Core
1313
using ..Convert
14+
using ..GIL
1415
using ..PyMacro
1516

1617
import ..PythonCall:

test/Wrap.jl

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,18 @@ end
122122
@testset "iterate keys" begin
123123
@test collect(keys(z)) == ["foo"]
124124
end
125+
@testset "complete keys without GIL" begin
126+
using REPL
127+
completion_count = PythonCall.GIL.@unlock begin
128+
task = @async begin
129+
completions, _, _ = REPL.REPLCompletions.completions("y[", 2, @__MODULE__)
130+
length(completions)
131+
end
132+
wait(task)
133+
fetch(task)
134+
end
135+
@test completion_count == 1
136+
end
125137
@testset "getindex" begin
126138
@test z["foo"] === 12
127139
@test_throws KeyError z["bar"]

0 commit comments

Comments
 (0)