Skip to content

Instantly share code, notes, and snippets.

@stevengj
Created March 7, 2018 19:45
Show Gist options
  • Save stevengj/a07601824ad607829455651b45bd0a97 to your computer and use it in GitHub Desktop.
Save stevengj/a07601824ad607829455651b45bd0a97 to your computer and use it in GitHub Desktop.
module that makes 0.7 hang
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