Created
March 7, 2018 19:45
-
-
Save stevengj/a07601824ad607829455651b45bd0a97 to your computer and use it in GitHub Desktop.
module that makes 0.7 hang
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module Bug | |
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!, | |
append!, insert!, prepend!, mimewritable, unsafe_convert | |
import Base: pushfirst!, popfirst! | |
import Base: sigatomic_begin, sigatomic_end | |
import Base.Iterators: filter | |
const libpython = "foo" | |
macro pysym(func) | |
:(($(esc(func)), libpython)) | |
end | |
macro pyglobal(name) | |
:(cglobal(($(esc(name)), libpython))) | |
end | |
macro pyglobalobj(name) | |
:(cglobal(($(esc(name)), libpython), PyObject_struct)) | |
end | |
macro pyglobalobjptr(name) | |
:(unsafe_load(cglobal(($(esc(name)), libpython), Ptr{PyObject_struct}))) | |
end | |
const Py_hash_t = Clong | |
struct PyObject_struct | |
ob_refcnt::Int | |
ob_type::Ptr{Cvoid} | |
end | |
const PyPtr = Ptr{PyObject_struct} # type for PythonObject* in ccall | |
const PyPtr_NULL = PyPtr(C_NULL) | |
mutable struct PyObject | |
o::PyPtr # the actual PyObject* | |
function PyObject(o::PyPtr) | |
po = new(o) | |
finalizer(pydecref, po) | |
return po | |
end | |
end | |
PyNULL() = PyObject(PyPtr_NULL) | |
function pydecref(o::PyObject) | |
ccall(@pysym(:Py_DecRef), Cvoid, (PyPtr,), o.o) | |
o.o = PyPtr_NULL | |
o | |
end | |
# conversion to pass PyObject as ccall arguments: | |
unsafe_convert(::Type{PyPtr}, po::PyObject) = po.o | |
# extract function name from ccall((@pysym :Foo)...) or ccall(:Foo,...) exprs | |
callsym(s::Symbol) = s | |
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 | |
# Macros for common pyerr_check("Foo", ccall((@pysym :Foo), ...)) pattern. | |
macro pycheck(ex) | |
:(pyerr_check($(string(callsym(ex))), $(esc(ex)))) | |
end | |
# Macros to check that ccall((@pysym :Foo), ...) returns value != bad | |
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") | |
end | |
val | |
end | |
end | |
macro pycheckn(ex) | |
:(@pycheckv $(esc(ex)) C_NULL) | |
end | |
macro pycheckz(ex) | |
:(@pycheckv $(esc(ex)) -1) | |
end | |
const TypeTuple = Union{Type,NTuple{N, Type}} where {N} | |
abstract type PyAny end | |
typetuple(Ts) = Tuple{Ts...} | |
function pysequence_query(o::PyObject) | |
if pyisinstance(o, @pyglobalobj :PyTuple_Type) | |
len = @pycheckz ccall((@pysym :PySequence_Size), Int, (PyPtr,), o) | |
return typetuple([pytype_query(PyObject(ccall((@pysym :PySequence_GetItem), PyPtr, (PyPtr,Int), o,i-1)), PyAny) for i = 1:len]) | |
else | |
return nothing | |
end | |
end | |
macro return_not_None(ex) | |
quote | |
T = $(esc(ex)) | |
if T != Union{} | |
return T | |
end | |
end | |
end | |
function pytype_query(o::PyObject, default::TypeTuple=PyObject) | |
@return_not_None pyint_query(o) | |
pyisinstance(o, npy_bool) && return Bool | |
@return_not_None pyfloat_query(o) | |
@return_not_None pycomplex_query(o) | |
@return_not_None pystring_query(o) | |
@return_not_None pyfunction_query(o) | |
@return_not_None pydate_query(o) | |
@return_not_None pydict_query(o) | |
@return_not_None pysequence_query(o) | |
@return_not_None pyptr_query(o) | |
@return_not_None pynothing_query(o) | |
@return_not_None pymp_query(o) | |
for (py,jl) in pytype_queries | |
if pyisinstance(o, py) | |
return jl | |
end | |
end | |
return default | |
end | |
function convert(::Type{PyAny}, o::PyObject) | |
if (o.o == C_NULL) | |
return o | |
end | |
try | |
T = pytype_query(o) | |
if T == PyObject && is_pyjlwrap(o) | |
return unsafe_pyjlwrap_to_objref(o.o) | |
end | |
convert(T, o) | |
catch | |
pyerr_clear() # just in case | |
o | |
end | |
end | |
(o::PyObject)(args...; kws...) = pycall(o, PyAny, args...; kws...) | |
PyAny(o::PyObject) = convert(PyAny, o) | |
const Py_LT = Cint(0) | |
const Py_LE = Cint(1) | |
const Py_EQ = Cint(2) | |
const Py_NE = Cint(3) | |
const Py_GT = Cint(4) | |
const Py_GE = Cint(5) | |
import Base: <, <=, ==, !=, >, >=, isequal, isless | |
for (op,py) in ((:<, Py_LT), (:<=, Py_LE), (:(==), Py_EQ), (:!=, Py_NE), | |
(:>, Py_GT), (:>=, Py_GE), (:isequal, Py_EQ), (:isless, Py_LT)) | |
println(stderr, "generating $op from $py") | |
flush(stderr) | |
@eval function $op(o1::PyObject, o2::PyObject) | |
if o1.o == C_NULL || o2.o == C_NULL | |
return $(py==Py_EQ || py==Py_NE || op==:isless ? :($op(o1.o, o2.o)) : false) | |
elseif is_pyjlwrap(o1) && is_pyjlwrap(o2) | |
return $op(unsafe_pyjlwrap_to_objref(o1.o), | |
unsafe_pyjlwrap_to_objref(o2.o)) | |
else | |
if $(op == :isless || op == :isequal) | |
return Bool(@pycheckz ccall((@pysym :PyObject_RichCompareBool), Cint, | |
(PyPtr, PyPtr, Cint), o1, o2, $py)) | |
else # other operations may return a PyObject | |
return PyAny(PyObject(@pycheckn ccall((@pysym :PyObject_RichCompare), PyPtr, | |
(PyPtr, PyPtr, Cint), o1, o2, $py))) | |
end | |
end | |
end | |
if op != :isequal | |
@eval begin | |
$op(o1::PyObject, o2::Any) = $op(o1, PyObject(o2)) | |
$op(o1::Any, o2::PyObject) = $op(PyObject(o1), o2) | |
end | |
end | |
end | |
end # module PyCall |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment