diff --git a/src/PyCall.jl b/src/PyCall.jl index 2450a297..bc91be3a 100644 --- a/src/PyCall.jl +++ b/src/PyCall.jl @@ -24,13 +24,6 @@ if isdefined(Base, :hasproperty) # Julia 1.2 import Base: hasproperty end -# Python C API is not interrupt-safe. In principle, we should -# use sigatomic for every ccall to the Python library, but this -# should really be fixed in Julia (#2622). However, we will -# use the sigatomic_begin/end functions to protect pycall and -# similar long-running (or potentially long-running) code. -import Base: sigatomic_begin, sigatomic_end - import Conda import MacroTools # because of issue #270 import Base.Iterators: filter diff --git a/src/exception.jl b/src/exception.jl index a5a465b0..7ced7a09 100644 --- a/src/exception.jl +++ b/src/exception.jl @@ -70,6 +70,18 @@ callsym(s::QuoteNode) = s.value import Base.Meta.isexpr callsym(ex::Expr) = isexpr(ex,:macrocall,2) ? callsym(ex.args[2]) : isexpr(ex,:ccall) ? callsym(ex.args[1]) : ex +""" + _handle_error(msg) + +Throw a PyError if available, otherwise throw ErrorException. +This is a hack to manually do the optimization described in +https://github.com/JuliaLang/julia/issues/29688 +""" +@noinline function _handle_error(msg) + pyerr_check(msg) + error(msg, " failed") +end + # Macros for common pyerr_check("Foo", ccall((@pysym :Foo), ...)) pattern. macro pycheck(ex) :(pyerr_check($(string(callsym(ex))), $(esc(ex)))) @@ -80,9 +92,7 @@ macro pycheckv(ex, bad) quote val = $(esc(ex)) if val == $(esc(bad)) - # throw a PyError if available, otherwise throw ErrorException - pyerr_check($(string(callsym(ex)))) - error($(string(callsym(ex))), " failed") + _handle_error($(string(callsym(ex)))) end val end diff --git a/src/pyeval.jl b/src/pyeval.jl index 8e8a6b3f..4eca19d5 100644 --- a/src/pyeval.jl +++ b/src/pyeval.jl @@ -31,17 +31,15 @@ pynamespace(m::Module) = # and a current "file name" to use for stack traces function pyeval_(s::AbstractString, globals=pynamespace(Main), locals=pynamespace(Main), input_type=Py_eval_input, fname="PyCall") - sigatomic_begin() - try - o = PyObject(@pycheckn ccall((@pysym :Py_CompileString), PyPtr, - (Cstring, Cstring, Cint), - s, fname, input_type)) - return PyObject(@pycheckn ccall((@pysym :PyEval_EvalCode), - PyPtr, (PyPtr, PyPtr, PyPtr), - o, globals, locals)) - finally - sigatomic_end() + o = PyObject(@pycheckn ccall((@pysym :Py_CompileString), PyPtr, + (Cstring, Cstring, Cint), + s, fname, input_type)) + ptr = disable_sigint() do + @pycheckn ccall((@pysym :PyEval_EvalCode), + PyPtr, (PyPtr, PyPtr, PyPtr), + o, globals, locals) end + return PyObject(ptr) end """ diff --git a/src/pyfncall.jl b/src/pyfncall.jl index 1fe7e804..fc07d8e8 100644 --- a/src/pyfncall.jl +++ b/src/pyfncall.jl @@ -39,14 +39,11 @@ Sets `ret.o` to the result of the call, and returns `ret::PyObject`. """ function __pycall!(ret::PyObject, pyargsptr::PyPtr, o::Union{PyObject,PyPtr}, kw::Union{Ptr{Cvoid}, PyObject}) - sigatomic_begin() - try + disable_sigint() do retptr = @pycheckn ccall((@pysym :PyObject_Call), PyPtr, (PyPtr,PyPtr,PyPtr), o, pyargsptr, kw) pydecref_(ret) setfield!(ret, :o, retptr) - finally - sigatomic_end() end return ret #::PyObject end diff --git a/src/pyiterator.jl b/src/pyiterator.jl index 6b743cad..85c909aa 100644 --- a/src/pyiterator.jl +++ b/src/pyiterator.jl @@ -5,14 +5,11 @@ Base.IteratorSize(::Type{PyObject}) = Base.SizeUnknown() function _start(po::PyObject) - sigatomic_begin() - try + disable_sigint() do o = PyObject(@pycheckn ccall((@pysym :PyObject_GetIter), PyPtr, (PyPtr,), po)) nxt = PyObject(@pycheck ccall((@pysym :PyIter_Next), PyPtr, (PyPtr,), o)) return (nxt,o) - finally - sigatomic_end() end end @@ -76,12 +73,9 @@ _start(piter::PyIterator) = _start(piter.o) function Base.iterate(piter::PyIterator{T}, s=_start(piter)) where {T} ispynull(s[1]) && return nothing - sigatomic_begin() - try + disable_sigint() do nxt = PyObject(@pycheck ccall((@pysym :PyIter_Next), PyPtr, (PyPtr,), s[2])) return (convert(T,s[1]), (nxt, s[2])) - finally - sigatomic_end() end end function Base.iterate(po::PyObject, s=_start(po))