Skip to content

Commit 896864d

Browse files
author
Christopher Doris
committed
julia arrays now support buffer and array interface
1 parent a326457 commit 896864d

File tree

8 files changed

+504
-38
lines changed

8 files changed

+504
-38
lines changed

src/PyObject.jl

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ Base.show(io::IO, o::PyObject) = begin
6262
end
6363

6464
function Base.show(io::IO, ::MIME"text/plain", o::PyObject)
65+
prefix = get(io, :typeinfo, Any) != typeof(o)
6566
h, w = displaysize(io)
6667
h -= 3
6768
x = try
@@ -73,26 +74,29 @@ function Base.show(io::IO, ::MIME"text/plain", o::PyObject)
7374
if '\n' x
7475
# multiple lines
7576
# each one is truncated to one screen width
76-
print(io, "py:")
77-
h -= 1
77+
if prefix
78+
print(io, "py:")
79+
h -= 1
80+
end
7881
xs = split(x, '\n')
79-
printlines(xs) =
80-
for x in xs
82+
printlines(xs, nl=true) =
83+
for (i,x) in enumerate(xs)
84+
(nl || i>1) && print(io, '\n')
8185
if length(x) w
82-
print(io, '\n', x)
86+
print(io, x)
8387
else
84-
print(io, '\n', x[1:nextind(x, 0, w-1)], '')
88+
print(io, x[1:nextind(x, 0, w-1)], '')
8589
end
8690
end
8791
if length(xs) h
8892
# all lines fit on screen
89-
printlines(xs)
93+
printlines(xs, prefix)
9094
else
9195
# too many lines, skip the middle ones
9296
h -= 1
9397
h2 = cld(h, 2)
9498
h3 = (length(xs)+1)-(h-h2)
95-
printlines(xs[1:h2])
99+
printlines(xs[1:h2], prefix)
96100
linelen = min(
97101
checkbounds(Bool, xs, h2) ? length(xs[h2]) : 0,
98102
checkbounds(Bool, xs, h3) ? length(xs[h3]) : 0,
@@ -103,14 +107,16 @@ function Base.show(io::IO, ::MIME"text/plain", o::PyObject)
103107
print(io, "\n", pad > 0 ? " "^pad : "", msg)
104108
printlines(xs[h3:end])
105109
end
106-
elseif length(x) w-4
110+
elseif length(x) (prefix ? w-4 : w)
107111
# one short line
108-
print(io, "py: ", x)
112+
print(io, prefix ? "py: " : "", x)
109113
return
110114
else
111115
# one long line
112-
println(io, "py:")
113-
h -= 1
116+
if prefix
117+
println(io, "py:")
118+
h -= 1
119+
end
114120
a = h * w
115121
if length(x) a
116122
# whole string fits on screen
@@ -127,7 +133,7 @@ function Base.show(io::IO, ::MIME"text/plain", o::PyObject)
127133
end
128134
end
129135
else
130-
print(io, "py: ", x)
136+
print(io, prefix ? "py: " : "", x)
131137
end
132138
end
133139

src/old/PyObjectArray.jl renamed to src/PyObjectArray.jl

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
"""
2+
PyObjectArray(undef, dims...)
3+
PyObjectArray(array)
4+
5+
An array of `PyObject`s which supports the Python buffer protocol.
6+
7+
Internally, the objects are stored as an array of pointers.
8+
"""
19
mutable struct PyObjectArray{N} <: AbstractArray{PyObject, N}
210
ptrs :: Array{CPyPtr, N}
311
function PyObjectArray{N}(::UndefInitializer, dims::NTuple{N,Integer}) where {N}
@@ -34,10 +42,11 @@ function Base.getindex(x::PyObjectArray, i::Integer...)
3442
end
3543

3644
function Base.setindex!(x::PyObjectArray, _v, i::Integer...)
37-
v = convert(PyObject, _v)
45+
vo = C.PyObject_From(_v)
46+
isnull(vo) && pythrow()
3847
C.Py_DecRef(x.ptrs[i...])
39-
pyincref!(v)
40-
x.ptrs[i...] = pyptr(v)
48+
C.Py_IncRef(vo)
49+
x.ptrs[i...] = vo
4150
x
4251
end
4352

@@ -46,3 +55,15 @@ function Base.deleteat!(x::PyObjectArray, i::Integer)
4655
deleteat!(x.ptrs, i)
4756
x
4857
end
58+
59+
C._pyjlarray_get_buffer(o, buf, flags, x::PyObjectArray) =
60+
C.pyjl_get_buffer_impl(o, buf, flags, pointer(x.ptrs), sizeof(CPyPtr), length(x), ndims(x), "O", size(x), strides(x.ptrs), true)
61+
62+
C._pyjlarray_array_interface(x::PyObjectArray) =
63+
C.PyDict_From(Dict(
64+
"shape" => size(x),
65+
"typestr" => "|O",
66+
"data" => (UInt(pointer(x.ptrs)), false),
67+
"strides" => strides(x.ptrs) .* sizeof(CPyPtr),
68+
"version" => 3,
69+
))

src/Python.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ include("PySet.jl")
9494
include("PyIterable.jl")
9595
include("PyIO.jl")
9696
include("PyBuffer.jl")
97+
include("PyObjectArray.jl")
9798
include("PyPandasDataFrame.jl")
9899

99100
include("julia.jl")

src/cpython/arg.jl

Lines changed: 25 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -68,42 +68,51 @@ end
6868
struct NODEFAULT end
6969

7070
PyArg_Find(args::PyPtr, kwargs::PyPtr, i::Union{Int,Nothing}, k::Union{String,Nothing}) =
71-
if i !== nothing && !isnull(args) && 0 i PyTuple_Size(args)
71+
if i !== nothing && !isnull(args) && 0 i < PyTuple_Size(args)
7272
return PyTuple_GetItem(args, i)
7373
elseif k !== nothing && !isnull(kwargs) && (ro = PyDict_GetItemString(kwargs, k)) != PyPtr()
7474
return ro
7575
else
7676
return PyPtr()
7777
end
7878

79-
PyArg_GetArg(::Type{T}, name::String, args::PyPtr, kwargs::PyPtr=PyPtr(), i::Union{Int,Nothing}=nothing, k::Union{String,Nothing}=nothing, d::Union{T,NODEFAULT}=NODEFAULT()) where {T} = begin
79+
"""
80+
PyArg_GetArg(T, funcname, [args, i], [kwargs, k], [default])
81+
82+
Attempt to find and convert the specified argument to a `T`. Return 0 on success, in which case the result can be retrieved with `takeresult(T)`. Return -1 on failure, with a Python error set.
83+
84+
- `funcname::String` is the name of the function this is used in, for constructing error messages.
85+
- `args::PyPtr` is a tuple of arguments, and `i::Int` an index.
86+
- `kwargs::PyPtr` is a dict of keyword arguments, and `k::String` a key.
87+
- `default` specifies a default value if the argument is not found. NOTE: It need not be a `T`, so when retrieving the result use `takeresult(Union{T,typeof(default)})`.
88+
"""
89+
PyArg_GetArg(::Type{T}, name::String, args::PyPtr, i::Union{Int,Nothing}, kwargs::PyPtr, k::Union{String,Nothing}, d=NODEFAULT()) where {T} = begin
8090
ro = PyArg_Find(args, kwargs, i, k)
8191
if isnull(ro)
82-
if k !== nothing
92+
if d !== NODEFAULT()
93+
putresult(d)
94+
return 0
95+
elseif k !== nothing
8396
PyErr_SetString(PyExc_TypeError(), "$name() did not get required argument '$k'")
84-
elseif i !== nothing
97+
return -1
98+
elseif i !== nothing && i 0
8599
PyErr_SetString(PyExc_TypeError(), "$name() takes at least $(i+1) arguments (got $(isnull(args) ? 0 : PyTuple_Size(args)))")
100+
return -1
86101
else
87102
error("impossible to satisfy this argument")
88103
end
89-
return -1
90104
end
91105
r = PyObject_TryConvert(ro, T)
92106
if r == -1
93107
return -1
94108
elseif r == 0
95-
if d === NODEFAULT()
96-
PyErr_SetString(PyExc_TypeError(), "Argument $(k !== nothing ? "'$k'" : i !== nothing ? "$i" : error("impossible")) to $name() must be convertible to a Julia '$T'")
97-
return -1
98-
else
99-
putresult(d)
100-
return 0
101-
end
109+
PyErr_SetString(PyExc_TypeError(), "Argument $(k !== nothing ? "'$k'" : i !== nothing ? "$i" : error("impossible")) to $name() must be convertible to a Julia '$T'")
110+
return -1
102111
else
103112
return 0
104113
end
105114
end
106-
PyArg_GetArg(::Type{T}, name::String, args::PyPtr, i::Union{Int,Nothing}, k::Union{String,Nothing}=nothing, d::Union{T,NODEFAULT}=NODEFAULT()) where {T} =
107-
PyArg_GetArg(T, name, args, PyPtr(), i, k, d)
108-
PyArg_GetArg(::Type{T}, name::String, args::PyPtr, kwargs::PyPtr, k::Union{String,Nothing}, d::Union{T, NODEFAULT}=NODEFAULT()) where {T} =
109-
PyArg_GetArg(T, name, args, kwargs, nothing, k, d)
115+
PyArg_GetArg(::Type{T}, name::String, args::PyPtr, i::Int, d=NODEFAULT()) where {T} =
116+
PyArg_GetArg(T, name, args, i, PyPtr(), nothing, d)
117+
PyArg_GetArg(::Type{T}, name::String, kwargs::PyPtr, k::String, d=NODEFAULT()) where {T} =
118+
PyArg_GetArg(T, name, PyPtr(), nothing, kwargs, k, d)

src/cpython/juliaany.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,9 @@ PyJuliaValue_From(x) = PyJuliaAnyValue_New(x)
4141

4242
pyjlany_repr(xo::PyPtr) = try
4343
x = PyJuliaValue_GetValue(xo)
44-
s = "<jl $(repr(x))>"
44+
# s = "<jl $(repr(x))>"
45+
s = sprint((io,x)->show(io,MIME"text/plain"(),x), x, context=:limit=>true)
46+
s = string("jl:", '\n' in s ? '\n' : ' ', s)
4547
PyUnicode_From(s)
4648
catch err
4749
PyErr_SetJuliaError(err)

0 commit comments

Comments
 (0)