-
Notifications
You must be signed in to change notification settings - Fork 78
Expand file tree
/
Copy pathtypes.jl
More file actions
241 lines (199 loc) · 7.51 KB
/
types.jl
File metadata and controls
241 lines (199 loc) · 7.51 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
# Core
"""
Py(x)
Convert `x` to a Python object, of type `Py`.
Conversion happens according to [these rules](@ref jl2py-conversion).
Such an object supports attribute access (`obj.attr`), indexing (`obj[idx]`), calling
(`obj(arg1, arg2)`), iteration (`for x in obj`), arithmetic (`obj + obj2`) and comparison
(`obj > obj2`), among other things. These operations convert all their arguments to `Py` and
return `Py`.
"""
mutable struct Py
ptr::Ptr{Cvoid}
Py(::Val{:new}, ptr::Ptr) = finalizer(Core.py_finalizer, new(Ptr{Cvoid}(ptr)))
end
"""
PyException(x)
Wraps the Python exception `x` as a Julia `Exception`.
"""
mutable struct PyException <: Exception
_t::Py
_v::Py
_b::Py
_isnormalized::Bool
end
"""
pybuiltins
An object whose fields are the Python builtins, of type [`Py`](@ref PythonCall.Py).
For example `pybuiltins.None`, `pybuiltins.int`, `pybuiltins.ValueError`.
"""
baremodule pybuiltins end
# Wrap
"""
PyArray{T,N,F}(x; copy=true, array=true, buffer=true)
Wrap the Python array `x` as a Julia `AbstractArray{T,N}`.
The input `x` can be `bytes`, `bytearray`, `array.array`, `numpy.ndarray` or anything satisfying the buffer protocol (if `buffer=true`) or the numpy array interface (if `array=true`).
If `copy=false` then the resulting array is guaranteed to directly wrap the data in `x`. If `copy=true` then a copy is taken if necessary to produce an array.
The type parameters are all optional, and are:
- `T`: The element type.
- `N`: The number of dimensions.
- `F`: Tuple of symbols, including:
- `:mutable`: The array is mutable.
- `:linear`: Supports fast linear indexing.
- `:contiguous`: Data is F-contiguous. Implies `:linear`.
"""
struct PyArray{T,N,F} <: AbstractArray{T,N}
ptr::Ptr{Cvoid} # pointer to the data
length::Int # length of the array
size::NTuple{N,Int} # size of the array
strides::NTuple{N,Int} # strides (in bytes) between elements
py::Py # underlying python object
handle::Py # the data in this array is valid as long as this handle is alive
function PyArray{T,N,F}(
::Val{:new},
ptr::Ptr,
size::NTuple{N,Int},
strides::NTuple{N,Int},
py::Py,
handle::Py,
) where {T,N,F}
T isa Type || error("T must be a Type")
N isa Int || error("N must be an Int")
F isa Tuple{Vararg{Symbol}} || error("F must be a tuple of Symbols")
for flag in F
flag in (:mutable, :linear, :contiguous, :copy, :nocopy) ||
error("invalid entry of F: $(repr(flag))")
end
new{T,N,F}(ptr, prod(size), size, strides, py, handle)
end
end
"""
PyDict{K=Py,V=Py}([x])
Wraps the Python dict `x` (or anything satisfying the mapping interface) as an `AbstractDict{K,V}`.
If `x` is not a Python object, it is converted to one using `pydict`.
"""
struct PyDict{K,V} <: AbstractDict{K,V}
py::Py
PyDict{K,V}(x = pydict()) where {K,V} = new{K,V}(ispy(x) ? Py(x) : pydict(x))
end
"""
PyIO(x; own=false, text=missing, line_buffering=false, buflen=4096)
Wrap the Python IO stream `x` as a Julia IO stream.
When this goes out of scope and is finalized, it is automatically flushed. If `own=true` then it is also closed.
If `text=false` then `x` must be a binary stream and arbitrary binary I/O is possible.
If `text=true` then `x` must be a text stream and only UTF-8 must be written (i.e. use `print` not `write`).
If `text` is not specified then it is chosen automatically.
If `x` is a text stream and you really need a binary stream, then often `PyIO(x.buffer)` will work.
If `line_buffering=true` then output is flushed at each line.
For efficiency, reads and writes are buffered before being sent to `x`.
The size of the buffers is `buflen`.
The buffers are cleared using `flush`.
"""
mutable struct PyIO <: IO
py::Py
# true to close the file automatically
own::Bool
# true if `o` is text, false if binary
text::Bool
# true to flush whenever '\n' or '\r' is encountered
line_buffering::Bool
# true if we are definitely at the end of the file; false if we are not or don't know
eof::Bool
# input buffer
ibuflen::Int
ibuf::Vector{UInt8}
# output buffer
obuflen::Int
obuf::Vector{UInt8}
function PyIO(
x;
own::Bool = false,
text::Union{Missing,Bool} = missing,
buflen::Integer = 4096,
ibuflen::Integer = buflen,
obuflen::Integer = buflen,
line_buffering::Bool = false,
)
if text === missing
text = pyhasattr(x, "encoding")
end
buflen = convert(Int, buflen)
buflen > 0 || error("buflen must be positive")
ibuflen = convert(Int, ibuflen)
ibuflen > 0 || error("ibuflen must be positive")
obuflen = convert(Int, obuflen)
obuflen > 0 || error("obuflen must be positive")
new(Py(x), own, text, line_buffering, false, ibuflen, UInt8[], obuflen, UInt8[])
end
end
"""
PyIterable{T=Py}(x)
This object iterates over iterable Python object `x`, yielding values of type `T`.
"""
struct PyIterable{T}
py::Py
PyIterable{T}(x) where {T} = new{T}(Py(x))
end
"""
PyList{T=Py}([x])
Wraps the Python list `x` (or anything satisfying the sequence interface) as an `AbstractVector{T}`.
If `x` is not a Python object, it is converted to one using `pylist`.
"""
struct PyList{T} <: AbstractVector{T}
py::Py
PyList{T}(x = pylist()) where {T} = new{T}(ispy(x) ? Py(x) : pylist(x))
end
"""
PyTable(x)
Wrap `x` as a Tables.jl-compatible table.
`PyTable` is an abstract type. See [`PyPandasDataFrame`](@ref) for a concrete example.
"""
abstract type PyTable end
"""
PyPandasDataFrame(x; [indexname::Union{Nothing,Symbol}], [columnnames::Function], [columntypes::Function])
Wraps the pandas DataFrame `x` as a Tables.jl-compatible table.
- `indexname`: The name of the column including the index. The default is `nothing`, meaning
to exclude the index.
- `columnnames`: A function mapping the Python column name (a `Py`) to the Julia one (a
`Symbol`). The default is `x -> Symbol(x)`.
- `columntypes`: A function taking the column name (a `Symbol`) and returning either the
desired element type of the column, or `nothing` to indicate automatic inference.
"""
struct PyPandasDataFrame <: PyTable
py::Py
indexname::Union{Symbol,Nothing}
columnnames::Function # Py -> Symbol
columntypes::Function # Symbol -> Union{Type,Nothing}
function PyPandasDataFrame(
x;
indexname::Union{Symbol,Nothing} = nothing,
columnnames::Function = x -> Symbol(x),
columntypes::Function = x -> nothing,
)
new(Py(x), indexname, columnnames, columntypes)
end
end
"""
PySet{T=Py}([x])
Wraps the Python set `x` (or anything satisfying the set interface) as an `AbstractSet{T}`.
If `x` is not a Python object, it is converted to one using `pyset`.
"""
struct PySet{T} <: AbstractSet{T}
py::Py
PySet{T}(x = pyset()) where {T} = new{T}(ispy(x) ? Py(x) : pyset(x))
end
"""
PyObjectArray(undef, dims...)
PyObjectArray(array)
An array of `Py`s which supports the Python buffer protocol.
Internally, the objects are stored as an array of pointers.
"""
mutable struct PyObjectArray{N} <: AbstractArray{Py,N}
ptrs::Array{Ptr{Cvoid},N}
function PyObjectArray{N}(::UndefInitializer, dims::NTuple{N,Integer}) where {N}
x = new{N}(fill(C_NULL, dims))
finalizer(JlWrap.pyobjectarray_finalizer, x)
end
end
const PyObjectVector = PyObjectArray{1}
const PyObjectMatrix = PyObjectArray{2}