Skip to content

Commit

Permalink
WIP: 0.7 compatibility (#469)
Browse files Browse the repository at this point in the history
* silence 0.7 deprecation warnings

* fixed missing compat calls
  • Loading branch information
stevengj authored Mar 8, 2018
1 parent ad89538 commit 55648f2
Show file tree
Hide file tree
Showing 19 changed files with 215 additions and 231 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ Here are solutions to some common problems:
* As mentioned above, use `foo[:bar]` and `foo[:bar](...)` rather than `foo.bar` and `foo.bar(...)`,
respectively, to access attributes and methods of Python objects.

* By default, PyCall [doesn't include the current directory in the Python search path](https://github.com/stevengj/PyCall.jl/issues/48). If you want to do that (in order to load a Python module from the current directory), just run `unshift!(PyVector(pyimport("sys")["path"]), "")`.
* By default, PyCall [doesn't include the current directory in the Python search path](https://github.com/stevengj/PyCall.jl/issues/48). If you want to do that (in order to load a Python module from the current directory), just run `pushfirst!(PyVector(pyimport("sys")["path"]), "")`.

## Python object interfaces

Expand Down
2 changes: 1 addition & 1 deletion REQUIRE
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
julia 0.6
Compat 0.32.0
Compat 0.55.0
Conda 0.2
MacroTools 0.3
28 changes: 14 additions & 14 deletions deps/build.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
# will probably need to re-run Pkg.build("PyCall").

using Compat
import Conda
import Conda, Compat.Libdl

struct UseCondaPython <: Exception end

Expand Down Expand Up @@ -53,9 +53,9 @@ function find_libpython(python::AbstractString)
v = pyconfigvar(python,"VERSION","")
libs = [ dlprefix*"python"*v, dlprefix*"python" ]
lib = pyconfigvar(python, "LIBRARY")
lib != "None" && unshift!(libs, splitext(lib)[1])
lib != "None" && pushfirst!(libs, splitext(lib)[1])
lib = pyconfigvar(python, "LDLIBRARY")
lib != "None" && unshift!(unshift!(libs, basename(lib)), lib)
lib != "None" && pushfirst!(pushfirst!(libs, basename(lib)), lib)
libs = unique(libs)

# it is ridiculous that it is this hard to find the path of libpython
Expand Down Expand Up @@ -106,13 +106,13 @@ function find_libpython(python::AbstractString)
end

if "yes" == get(ENV, "PYCALL_DEBUG_BUILD", "no") # print out extra info to help with remote debugging
println(STDERR, "------------------------------------- exceptions -----------------------------------------")
println(stderr, "------------------------------------- exceptions -----------------------------------------")
for s in error_strings
print(s, "\n\n")
end
println(STDERR, "---------------------------------- get_config_vars ---------------------------------------")
print(STDERR, read(`python -c "import distutils.sysconfig; print(distutils.sysconfig.get_config_vars())"`, String))
println(STDERR, "--------------------------------- directory contents -------------------------------------")
println(stderr, "---------------------------------- get_config_vars ---------------------------------------")
print(stderr, read(`python -c "import distutils.sysconfig; print(distutils.sysconfig.get_config_vars())"`, String))
println(stderr, "--------------------------------- directory contents -------------------------------------")
for libpath in libpaths
if isdir(libpath)
print(libpath, ":\n")
Expand All @@ -123,7 +123,7 @@ function find_libpython(python::AbstractString)
end
end
end
println(STDERR, "------------------------------------------------------------------------------------------")
println(stderr, "------------------------------------------------------------------------------------------")
end

error("""
Expand All @@ -150,18 +150,18 @@ wstringconst(s) = string("Base.cconvert(Cwstring, \"", escape_string(s), "\")")
# problems in the unlikely event of read-only directories.
function writeifchanged(filename, str)
if !isfile(filename) || read(filename, String) != str
info(abspath(filename), " has been updated")
Compat.@info string(abspath(filename), " has been updated")
write(filename, str)
else
info(abspath(filename), " has not changed")
Compat.@info string(abspath(filename), " has not changed")
end
end

try # make sure deps.jl file is removed on error
python = try
let py = get(ENV, "PYTHON", isfile("PYTHON") ? readchomp("PYTHON") :
(Compat.Sys.isunix() && !Compat.Sys.isapple()) || Sys.ARCH (:i686, :x86_64) ? "python" : ""),
vers = isempty(py) ? v"0.0" : convert(VersionNumber, pyconfigvar(py,"VERSION","0.0"))
vers = isempty(py) ? v"0.0" : VersionNumber(pyconfigvar(py,"VERSION","0.0"))
if vers < v"2.7"
if isempty(py)
throw(UseCondaPython())
Expand All @@ -182,10 +182,10 @@ try # make sure deps.jl file is removed on error
catch e1
if Sys.ARCH in (:i686, :x86_64)
if isa(e1, UseCondaPython)
info("Using the Python distribution in the Conda package by default.\n",
Compat.@info string("Using the Python distribution in the Conda package by default.\n",
"To use a different Python version, set ENV[\"PYTHON\"]=\"pythoncommand\" and re-run Pkg.build(\"PyCall\").")
else
info( "No system-wide Python was found; got the following error:\n",
Compat.@info string( "No system-wide Python was found; got the following error:\n",
"$e1\nusing the Python distribution in the Conda package")
end
abspath(Conda.PYTHONDIR, "python" * (Compat.Sys.iswindows() ? ".exe" : ""))
Expand Down Expand Up @@ -222,7 +222,7 @@ try # make sure deps.jl file is removed on error
# cache the Python version as a Julia VersionNumber
pyversion = VersionNumber(pyvar(python, "platform", "python_version()"))

info("PyCall is using $python (Python $pyversion) at $programname, libpython = $libpy_name")
Compat.@info "PyCall is using $python (Python $pyversion) at $programname, libpython = $libpy_name"

if pyversion < v"2.7"
error("Python 2.7 or later is required for PyCall")
Expand Down
6 changes: 4 additions & 2 deletions deps/depsutils.jl
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import Compat.Libdl

hassym(lib, sym) = Libdl.dlsym_e(lib, sym) != C_NULL

# call dlsym_e on a sequence of symbols and return the symbol that gives
Expand All @@ -20,9 +22,9 @@ end
function Py_SetPythonHome(libpy, PYTHONHOME, wPYTHONHOME, pyversion)
if !isempty(PYTHONHOME)
if pyversion.major < 3
ccall(Libdl.dlsym(libpy, :Py_SetPythonHome), Void, (Cstring,), PYTHONHOME)
ccall(Libdl.dlsym(libpy, :Py_SetPythonHome), Cvoid, (Cstring,), PYTHONHOME)
else
ccall(Libdl.dlsym(libpy, :Py_SetPythonHome), Void, (Ptr{Cwchar_t},), wPYTHONHOME)
ccall(Libdl.dlsym(libpy, :Py_SetPythonHome), Cvoid, (Ptr{Cwchar_t},), wPYTHONHOME)
end
end
end
Expand Down
52 changes: 23 additions & 29 deletions src/PyCall.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ __precompile__()

module PyCall

using Compat

export pycall, pyimport, pybuiltin, PyObject, PyReverseDims,
PyPtr, pyincref, pydecref, pyversion, PyArray, PyArray_Info,
pyerr_check, pyerr_clear, pytype_query, PyAny, @pyimport, PyDict,
Expand All @@ -14,24 +16,19 @@ import Base: size, ndims, similar, copy, getindex, setindex!, stride,
convert, pointer, summary, convert, show, haskey, keys, values,
eltype, get, delete!, empty!, length, isempty, start, done,
next, filter!, hash, splice!, pop!, ==, isequal, push!,
unshift!, shift!, append!, insert!, prepend!, mimewritable
append!, insert!, prepend!, mimewritable, unsafe_convert
import Compat: pushfirst!, popfirst!

# Python C API is not interrupt-save. In principle, we should
# 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

## Compatibility import for v0.5
using Compat
import Conda
import Base.unsafe_convert
import MacroTools # because of issue #270

if isdefined(Base, :Iterators)
import Base.Iterators: filter
end
import Base.Iterators: filter

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

Expand All @@ -47,7 +44,7 @@ include("startup.jl")
# while we're at it.
struct PyObject_struct
ob_refcnt::Int
ob_type::Ptr{Void}
ob_type::Ptr{Cvoid}
end

const PyPtr = Ptr{PyObject_struct} # type for PythonObject* in ccall
Expand All @@ -71,7 +68,7 @@ mutable struct PyObject
o::PyPtr # the actual PyObject*
function PyObject(o::PyPtr)
po = new(o)
finalizer(po, pydecref)
@compat finalizer(pydecref, po)
return po
end
end
Expand All @@ -93,13 +90,13 @@ someobject)`. This procedure will properly handle Python's reference counting
PyNULL() = PyObject(PyPtr_NULL)

function pydecref(o::PyObject)
ccall(@pysym(:Py_DecRef), Void, (PyPtr,), o.o)
ccall(@pysym(:Py_DecRef), Cvoid, (PyPtr,), o.o)
o.o = PyPtr_NULL
o
end

function pyincref_(o::PyPtr)
ccall((@pysym :Py_IncRef), Void, (PyPtr,), o)
ccall((@pysym :Py_IncRef), Cvoid, (PyPtr,), o)
return o
end

Expand Down Expand Up @@ -134,10 +131,10 @@ end
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{Cvoid},PyPtr}) =
t != C_NULL && ccall((@pysym :PyObject_IsInstance), Cint, (PyPtr,PyPtr), o, t) == 1

pyquery(q::Ptr{Void}, o::PyObject) =
pyquery(q::Ptr{Cvoid}, o::PyObject) =
ccall(q, Cint, (PyPtr,), o) == 1

# conversion to pass PyObject as ccall arguments:
Expand Down Expand Up @@ -347,19 +344,19 @@ end
# during import.

struct ACTIVATION_CONTEXT_BASIC_INFORMATION
handle::Ptr{Void}
handle::Ptr{Cvoid}
dwFlags::UInt32
end
const ActivationContextBasicInformation = 1
const QUERY_ACTCTX_FLAG_ACTCTX_IS_ADDRESS = 0x10
const PyActCtx = Ref{Ptr{Void}}(C_NULL)
const PyActCtx = Ref{Ptr{Cvoid}}(C_NULL)

function ComputePyActCtx()
if PyActCtx[] == C_NULL
some_address_in_libpython = @pyglobal(:PyImport_ImportModule)
ActCtxBasicInfo = Ref{ACTIVATION_CONTEXT_BASIC_INFORMATION}()
succeeded = ccall(:QueryActCtxW,stdcall,Bool,
(UInt32,Ptr{Void},Ptr{Void},Culong,Ptr{Void},Csize_t,Ptr{Csize_t}),
(UInt32,Ptr{Cvoid},Ptr{Cvoid},Culong,Ptr{Cvoid},Csize_t,Ptr{Csize_t}),
QUERY_ACTCTX_FLAG_ACTCTX_IS_ADDRESS, some_address_in_libpython,
C_NULL, ActivationContextBasicInformation, ActCtxBasicInfo,
sizeof(ActCtxBasicInfo), C_NULL)
Expand All @@ -370,17 +367,17 @@ end
end

function ActivatePyActCtx()
cookie = Ref{Ptr{Void}}()
cookie = Ref{Ptr{Cvoid}}()
succeeded = ccall(:ActivateActCtx,stdcall, Bool,
(Ptr{Void}, Ptr{Ptr{Void}}),
(Ptr{Cvoid}, Ptr{Ptr{Cvoid}}),
ComputePyActCtx(), cookie)
@assert succeeded
cookie[]
end

function DeactivatePyActCtx(cookie)
succeeded = ccall(:DeactivateActCtx, stdcall, Bool,
(UInt32, Ptr{Void}), 0, cookie)
(UInt32, Ptr{Cvoid}), 0, cookie)
@assert succeeded
end
else
Expand Down Expand Up @@ -614,15 +611,15 @@ function pyimport_conda(modulename::AbstractString, condapkg::AbstractString,
pyimport(modulename)
catch e
if conda
info("Installing $modulename via the Conda $condapkg package...")
Compat.@info "Installing $modulename via the Conda $condapkg package..."
isempty(channel) || Conda.add_channel(channel)
Conda.add(condapkg)
pyimport(modulename)
else
aconda = anaconda_conda()
if !isempty(aconda)
try
info("Installing $modulename via Anaconda's $aconda...")
Compat.@info "Installing $modulename via Anaconda's $aconda..."
isempty(channel) || run(`$aconda config --add channels $channel --force`)
run(`$aconda install -y $condapkg`)
catch e2
Expand Down Expand Up @@ -797,7 +794,7 @@ function splice!(a::PyObject, i::Integer)
end

pop!(a::PyObject) = splice!(a, length(a))
shift!(a::PyObject) = splice!(a, 1)
popfirst!(a::PyObject) = splice!(a, 1)

function empty!(a::PyObject)
for i in length(a):-1:1
Expand All @@ -819,7 +816,7 @@ function insert!(a::PyObject, i::Integer, item)
a
end

unshift!(a::PyObject, item) = insert!(a, 1, item)
pushfirst!(a::PyObject, item) = insert!(a, 1, item)

function prepend!(a::PyObject, items)
if isa(items,PyObject) && items.o == a.o
Expand Down Expand Up @@ -880,10 +877,7 @@ end
#########################################################################
# Expose Python docstrings to the Julia doc system

if isdefined(Docs, :getdoc) # Introduced in Julia .6
Docs.getdoc(o::PyObject) = Text(String(o["__doc__"]))
end

Docs.getdoc(o::PyObject) = Text(String(o["__doc__"]))

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

Expand Down
36 changes: 13 additions & 23 deletions src/callback.jl
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ function pymethod(f::Function, name::AbstractString, flags::Integer)
def = gensym("PyMethodDef")
@eval const $def = PyMethodDef[PyMethodDef($name, $f, $flags)]
PyObject(@pycheckn ccall((@pysym :PyCFunction_NewEx), PyPtr,
(Ptr{PyMethodDef}, Ptr{Void}, Ptr{Void}),
(Ptr{PyMethodDef}, Ptr{Cvoid}, Ptr{Cvoid}),
eval(def), C_NULL, C_NULL))
end

Expand All @@ -37,28 +37,18 @@ function _pyjlwrap_call(f, args_::PyPtr, kw_::PyPtr)
try
jlargs = julia_args(f, args)

# on 0.6 we need to use invokelatest to get execution in newest world
@static if isdefined(Base, :invokelatest)
if kw_ == C_NULL
ret = PyObject(Base.invokelatest(f, jlargs...))
else
kw = PyDict{Symbol,PyObject}(pyincref(kw_))
kwargs = [ (k,julia_kwarg(f,k,v)) for (k,v) in kw ]

# 0.6 `invokelatest` doesn't support kwargs, instead
# use a closure over kwargs. see:
# https://github.com/JuliaLang/julia/pull/22646
f_kw_closure() = f(jlargs...; kwargs...)
ret = PyObject(Core._apply_latest(f_kw_closure))
end
else # 0.5 support
if kw_ == C_NULL
ret = PyObject(f(jlargs...))
else
kw = PyDict{Symbol,PyAny}(pyincref(kw_))
kwargs = [ (k,julia_kwarg(f,k,v)) for (k,v) in kw ]
ret = PyObject(f(jlargs...; kwargs...))
end
# we need to use invokelatest to get execution in newest world
if kw_ == C_NULL
ret = PyObject(Base.invokelatest(f, jlargs...))
else
kw = PyDict{Symbol,PyObject}(pyincref(kw_))
kwargs = [ (k,julia_kwarg(f,k,v)) for (k,v) in kw ]

# 0.6 `invokelatest` doesn't support kwargs, instead
# use a closure over kwargs. see:
# https://github.com/JuliaLang/julia/pull/22646
f_kw_closure() = f(jlargs...; kwargs...)
ret = PyObject(Core._apply_latest(f_kw_closure))
end

return pystealref!(ret)
Expand Down
Loading

0 comments on commit 55648f2

Please sign in to comment.