Skip to content

Commit

Permalink
Merge pull request #144 from yuyichao/0.4-dev
Browse files Browse the repository at this point in the history
Fix 0.4-dev dlopen breakage
  • Loading branch information
stevengj committed May 22, 2015
2 parents 32b10a5 + a497bf9 commit 0823f7c
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 25 deletions.
32 changes: 20 additions & 12 deletions src/PyCall.jl
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import Base: size, ndims, similar, copy, getindex, setindex!, stride,

# Python C API is not interrupt-save. 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
# 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
Expand All @@ -28,10 +28,18 @@ if VERSION >= v"0.4.0-dev+3710"
else
const unsafe_convert = Base.convert
end
# This is not the exact version but should be closed enough
if VERSION >= v"0.4.0-dev+4922"
typealias HandleT Union(Libdl.DLHandle, Ptr{Void})
hdl_ptr(hdl::Libdl.DLHandle) = hdl.ptr
else
typealias HandleT Ptr{Void}
end
hdl_ptr(hdl::Ptr{Void}) = hdl

#########################################################################

# Mirror of C PyObject struct (for non-debugging Python builds).
# Mirror of C PyObject struct (for non-debugging Python builds).
# We won't actually access these fields directly; we'll use the Python
# C API for everything. However, we need to define a unique Ptr type
# for PyObject*, and we might as well define the actual struct layout
Expand Down Expand Up @@ -115,10 +123,10 @@ function pyincref(o::PyPtr)
PyObject(o)
end

pyisinstance(o::PyObject, t::PyObject) =
pyisinstance(o::PyObject, t::PyObject) =
t.o != C_NULL && ccall((@pysym :PyObject_IsInstance), Cint, (PyPtr,PyPtr), o, t.o) == 1

pyisinstance(o::PyObject, t::Union(Ptr{Void},PyPtr)) =
pyisinstance(o::PyObject, t::Union(Ptr{Void},PyPtr)) =
t != C_NULL && ccall((@pysym :PyObject_IsInstance), Cint, (PyPtr,PyPtr), o, t) == 1

pyquery(q::Ptr{Void}, o::PyObject) =
Expand Down Expand Up @@ -173,7 +181,7 @@ function pystring(o::PyObject)
end
return convert(AbstractString, PyObject(s))
end
end
end

function show(io::IO, o::PyObject)
print(io, "PyObject $(pystring(o))")
Expand Down Expand Up @@ -216,7 +224,7 @@ function ==(o1::PyObject, o2::PyObject)
return o1.o == o2.o
elseif is_pyjlwrap(o1)
if is_pyjlwrap(o2)
return unsafe_pyjlwrap_to_objref(o1.o) ==
return unsafe_pyjlwrap_to_objref(o1.o) ==
unsafe_pyjlwrap_to_objref(o2.o)
else
return false
Expand Down Expand Up @@ -302,7 +310,7 @@ const reserved = Set{ASCIIString}(["while", "if", "for", "try", "return", "break

function pywrap(o::PyObject, mname::Symbol=:__anon__)
@pyinitialize
members = convert(Vector{@compat Tuple{AbstractString,PyObject}},
members = convert(Vector{@compat Tuple{AbstractString,PyObject}},
pycall(inspect["getmembers"], PyObject, o))
filter!(m -> !(m[1] in reserved), members)
# Hack to create an anonymous bare module
Expand Down Expand Up @@ -385,7 +393,7 @@ function pycall(o::PyObject, returntype::TypeTuple, args...; kwargs...)
nargs = length(args)
sigatomic_begin()
try
arg = PyObject(@pycheckn ccall((@pysym :PyTuple_New), PyPtr, (Int,),
arg = PyObject(@pycheckn ccall((@pysym :PyTuple_New), PyPtr, (Int,),
nargs))
for i = 1:nargs
@pycheckzi ccall((@pysym :PyTuple_SetItem), Cint,
Expand All @@ -411,7 +419,7 @@ end
# Once Julia lets us overload ".", we will use [] to access items, but
# for now we can define "get"

function get(o::PyObject, returntype::TypeTuple, k, default)
function get(o::PyObject, returntype::TypeTuple, k, default)
r = ccall((@pysym :PyObject_GetItem), PyPtr, (PyPtr,PyPtr), o,PyObject(k))
if r == C_NULL
pyerr_clear()
Expand All @@ -421,8 +429,8 @@ function get(o::PyObject, returntype::TypeTuple, k, default)
end
end

get(o::PyObject, returntype::TypeTuple, k) =
convert(returntype, PyObject(@pycheckni ccall((@pysym :PyObject_GetItem),
get(o::PyObject, returntype::TypeTuple, k) =
convert(returntype, PyObject(@pycheckni ccall((@pysym :PyObject_GetItem),
PyPtr, (PyPtr,PyPtr), o, PyObject(k))))

get(o::PyObject, k, default) = get(o, PyAny, k, default)
Expand Down Expand Up @@ -474,7 +482,7 @@ const pyeval_fname = bytestring("PyCall.jl") # filename for pyeval

# evaluate a python string, returning PyObject, given a dictionary
# (string/symbol => value) of local variables to use in the expression
function pyeval_(s::AbstractString, locals::PyDict)
function pyeval_(s::AbstractString, locals::PyDict)
sb = bytestring(s) # use temp var to prevent gc before we are done with o
sigatomic_begin()
try
Expand Down
34 changes: 21 additions & 13 deletions src/pyinit.jl
Original file line number Diff line number Diff line change
Expand Up @@ -54,15 +54,15 @@ function dlopen_libpython(python::AbstractString)
ENV["PYTHONHOME"] = @windows? exec_prefix : pyconfigvar(python, "prefix") * ":" * exec_prefix
# Unfortunately, setting PYTHONHOME screws up Canopy's Python distro
try
run(pipe(`$python -c "import site"`, stdout=DevNull, stderr=DevNull))
run(pipe(`$python -c "import site"`, stdout=DevNull, stderr=DevNull))
catch
pop!(ENV, "PYTHONHOME")
pop!(ENV, "PYTHONHOME")
end
end
# TODO: look in python-config output? pyconfigvar("LDFLAGS")?
for lib in libs
for libpath in libpaths
if isfile(joinpath(libpath, lib))
if isfile(joinpath(libpath, lib))
try
return Libdl.dlopen(joinpath(libpath, lib),
Libdl.RTLD_LAZY|Libdl.RTLD_DEEPBIND|Libdl.RTLD_GLOBAL)
Expand All @@ -75,7 +75,7 @@ function dlopen_libpython(python::AbstractString)
# library path might be the wrong one if multiple python
# versions are installed (we prefer the one in LIBDIR):
for lib in libs
lib = splitext(lib)[1]
lib = splitext(lib)[1]
try
return Libdl.dlopen(lib, Libdl.RTLD_LAZY|Libdl.RTLD_DEEPBIND|Libdl.RTLD_GLOBAL)
end
Expand Down Expand Up @@ -129,7 +129,7 @@ Py_GetVersion(libpy=libpython) = bytestring(ccall(Libdl.dlsym(libpy, :Py_GetVers
# low-level initialization, given a pointer to dlopen result on libpython,
# or C_NULL if python symbols are in the global namespace:
# initialize the Python interpreter (no-op on subsequent calls)
function pyinitialize(libpy::Ptr{Void}, programname="")
function pyinitialize(libpy::HandleT, programname="")
global initialized
global finalized
if !initialized::Bool
Expand All @@ -147,7 +147,13 @@ function pyinitialize(libpy::Ptr{Void}, programname="")
# namespace. On windows, jl_exe_handle stores this, whereas on
# other systems jl_dl_handle is equivalent to dlopen(NULL) and stores
# this. (These are internal vars present in Julia 0.2/0.3/0.4.)
global const libpython = libpy == C_NULL ? unsafe_load(cglobal((@windows ? :jl_exe_handle : :jl_dl_handle), Ptr{Void})) : libpy
# libpython_hdl is whatever passed in to this function. Can be either
# a DLHandle or a Ptr{Void} on 0.4. This is the one that should be
# closed in finalizer.
# libpython is always a Ptr{Void}, which can be the handle passed in or
# the global julia handle
global const libpython_hdl = libpy
global const libpython = hdl_ptr(libpy) == C_NULL ? unsafe_load(cglobal((@windows ? :jl_exe_handle : :jl_dl_handle), Ptr{Void})) : hdl_ptr(libpy)

# cache the Python version as a Julia VersionNumber
global const pyversion = convert(VersionNumber, split(Py_GetVersion(libpython))[1])
Expand All @@ -158,7 +164,7 @@ function pyinitialize(libpy::Ptr{Void}, programname="")
# the PyMemberDef array must not be garbage-collected
global const pyjlwrap_membername = "jl_value"
global const pyjlwrap_doc = "Julia jl_value_t* (Any object)"
global const pyjlwrap_members =
global const pyjlwrap_members =
PyMemberDef[ PyMemberDef(pyjlwrap_membername,
T_PYSSIZET, sizeof_PyObject_HEAD, READONLY,
pyjlwrap_doc),
Expand All @@ -168,10 +174,10 @@ function pyinitialize(libpy::Ptr{Void}, programname="")
if !already_inited
if !isempty(pyprogramname)
if pyversion.major < 3
ccall((@pysym :Py_SetProgramName), Void, (Ptr{Uint8},),
ccall((@pysym :Py_SetProgramName), Void, (Ptr{Uint8},),
pyprogramname)
else
ccall((@pysym :Py_SetProgramName), Void, (Ptr{Cwchar_t},),
ccall((@pysym :Py_SetProgramName), Void, (Ptr{Cwchar_t},),
pyprogramname)
end
end
Expand Down Expand Up @@ -285,7 +291,7 @@ function pyinitialize(libpy::Ptr{Void}, programname="")

init_datetime()
pyjlwrap_init()

global const jl_FunctionType = pyjlwrap_type("PyCall.jl_Function",
t -> t.tp_call =
jl_Function_call_ptr)
Expand All @@ -301,7 +307,7 @@ function pyinitialize(libpy::Ptr{Void}, programname="")
argv = unsafe_convert(Ptr{Cwchar_t}, argv_s)
ccall(pysym(:PySys_SetArgvEx), Void, (Cint, Ptr{Ptr{Cwchar_t}}, Cint), 1, &argv, 0)
end

# Some Python code checks sys.ps1 to see if it is running
# interactively, and refuses to be interactive otherwise.
# (e.g. Matplotlib: see PyPlot#79)
Expand Down Expand Up @@ -331,7 +337,7 @@ function pyinitialize(python::AbstractString)
return
end

pyinitialize() = pyinitialize(get(ENV, "PYTHON", "python"))
pyinitialize() = pyinitialize(get(ENV, "PYTHON", "python"))
dlopen_libpython() = dlopen_libpython(get(ENV, "PYTHON", "python"))

# end the Python interpreter and free associated memory
Expand Down Expand Up @@ -359,7 +365,9 @@ function pyfinalize()
pygc_finalize()
gc() # collect/decref any remaining PyObjects
ccall((@pysym :Py_Finalize), Void, ())
dlclose(libpython)
if hdl_ptr(libpython_hdl) != C_NULL
dlclose(libpython_hdl)
end
initialized::Bool = false
finalized::Bool = true
end
Expand Down

0 comments on commit 0823f7c

Please sign in to comment.