Skip to content

Commit 3b0a0f4

Browse files
authored
type-stable PyError constructor (#1022)
* fix test_build to use PyCall's python * type-stable PyError constructor
1 parent d1764a6 commit 3b0a0f4

File tree

3 files changed

+25
-17
lines changed

3 files changed

+25
-17
lines changed

src/PyCall.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,7 @@ function _getproperty(o::PyObject, s::Union{AbstractString,Symbol})
299299
ispynull(o) && throw(ArgumentError("ref of NULL PyObject"))
300300
p = ccall((@pysym :PyObject_GetAttrString), PyPtr, (PyPtr, Cstring), o, s)
301301
if p == C_NULL && pyerr_occurred()
302-
e = pyerror("PyObject_GetAttrString")
302+
e = PyError("PyObject_GetAttrString")
303303
if PyPtr(e.T) != @pyglobalobjptr(:PyExc_AttributeError)
304304
throw(e)
305305
end
@@ -336,7 +336,7 @@ function _setproperty!(o::PyObject, s::Union{Symbol,AbstractString}, v)
336336
ispynull(o) && throw(ArgumentError("assign of NULL PyObject"))
337337
p = ccall((@pysym :PyObject_SetAttrString), Cint, (PyPtr, Cstring, PyPtr), o, s, PyObject(v))
338338
if p == -1 && pyerr_occurred()
339-
e = pyerror("PyObject_SetAttrString")
339+
e = PyError("PyObject_SetAttrString")
340340
if PyPtr(e.T) != @pyglobalobjptr(:PyExc_AttributeError)
341341
throw(e)
342342
end
@@ -507,7 +507,7 @@ function pyimport(name::AbstractString)
507507
o = _pyimport(name)
508508
if ispynull(o)
509509
if pyerr_occurred()
510-
e = pyerror("PyImport_ImportModule")
510+
e = PyError("PyImport_ImportModule")
511511
if pyisinstance(e.val, @pyglobalobjptr(:PyExc_ImportError))
512512
# Expand message to help with common user confusions.
513513
msg = """
@@ -553,7 +553,7 @@ or alternatively you can use the Conda package directly (via
553553
`using Conda` followed by `Conda.add` etcetera).
554554
"""
555555
end
556-
e = pyerror(string(e.msg, "\n\n", msg, "\n"), e)
556+
e = PyError(string(e.msg, "\n\n", msg, "\n"), e)
557557
end
558558
throw(e)
559559
else

src/exception.jl

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,19 @@ function show(io::IO, e::PyError)
4949
end
5050
end
5151

52+
# like pyerror(msg) but type-stable: always returns PyError
53+
function PyError(msg::AbstractString)
54+
ptype, pvalue, ptraceback = Ref{PyPtr}(), Ref{PyPtr}(), Ref{PyPtr}()
55+
# equivalent of passing C pointers &exc[1], &exc[2], &exc[3]:
56+
ccall((@pysym :PyErr_Fetch), Cvoid, (Ref{PyPtr},Ref{PyPtr},Ref{PyPtr}), ptype, pvalue, ptraceback)
57+
ccall((@pysym :PyErr_NormalizeException), Cvoid, (Ref{PyPtr},Ref{PyPtr},Ref{PyPtr}), ptype, pvalue, ptraceback)
58+
return PyError(msg, PyObject(ptype[]), PyObject(pvalue[]), PyObject(ptraceback[]))
59+
end
60+
61+
# replace the message from another error
62+
PyError(msg::AbstractString, e::PyError) =
63+
PyError(msg, e.T, e.val, e.traceback)
64+
5265
#########################################################################
5366
# Conversion of Python exceptions into Julia exceptions
5467

@@ -106,17 +119,12 @@ macro pycheckz(ex)
106119
:(@pycheckv $(esc(ex)) -1)
107120
end
108121

109-
function pyerror(msg::AbstractString)
110-
ptype, pvalue, ptraceback = Ref{PyPtr}(), Ref{PyPtr}(), Ref{PyPtr}()
111-
# equivalent of passing C pointers &exc[1], &exc[2], &exc[3]:
112-
ccall((@pysym :PyErr_Fetch), Cvoid, (Ref{PyPtr},Ref{PyPtr},Ref{PyPtr}), ptype, pvalue, ptraceback)
113-
ccall((@pysym :PyErr_NormalizeException), Cvoid, (Ref{PyPtr},Ref{PyPtr},Ref{PyPtr}), ptype, pvalue, ptraceback)
114-
pyerror(msg, PyObject(ptype[]), PyObject(pvalue[]), PyObject(ptraceback[]))
115-
end
122+
# like PyError(...) but type-unstable: may unwrap a PyJlError
123+
# if one was thrown by a nested pyjlwrap function.
116124

117-
function pyerror(msg::AbstractString, e::PyError)
125+
pyerror(msg::AbstractString) = pyerror(msg, PyError(msg))
126+
pyerror(msg::AbstractString, e::PyError) =
118127
pyerror(msg, e.T, e.val, e.traceback)
119-
end
120128

121129
function pyerror(msg::AbstractString, ptype::PyObject, pvalue::PyObject, ptraceback::PyObject)
122130
pargs = _getproperty(pvalue, "args")

test/test_build.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@ include(joinpath(dirname(@__FILE__), "..", "deps", "depsutils.jl"))
44
include(joinpath(dirname(@__FILE__), "..", "deps", "buildutils.jl"))
55

66
using Test
7+
using PyCall: python
78

89
@testset "find_libpython" begin
9-
for python in ["python", "python2", "python3"]
10-
# TODO: In Windows, word size should also be checked.
11-
Sys.iswindows() && break
10+
# TODO: In Windows, word size should also be checked.
11+
if !Sys.iswindows()
1212
if Sys.which(python) === nothing
1313
@info "$python not available; skipping test"
1414
else
@@ -37,7 +37,7 @@ using Test
3737
# Test the case `dlopen` failed to open the library.
3838
let err, msg
3939
@test try
40-
find_libpython("python"; _dlopen = (_...) -> error("dummy"))
40+
find_libpython(python; _dlopen = (_...) -> error("dummy"))
4141
false
4242
catch err
4343
err isa ErrorException

0 commit comments

Comments
 (0)