Skip to content

Commit a326457

Browse files
author
Christopher Doris
committed
dev
1 parent 5d10a34 commit a326457

File tree

7 files changed

+758
-20
lines changed

7 files changed

+758
-20
lines changed

juliapy/julia/__init__.py

Lines changed: 10 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
_CONFIG = dict()
1+
CONFIG = dict()
22

3-
def _init_():
3+
def init():
44
import os, os.path, sys, ctypes as c, types, shutil, subprocess
55
libpath = os.environ.get('JULIAPY_LIB')
66
if libpath is None:
@@ -12,20 +12,20 @@ def _init_():
1212
else:
1313
if not os.path.isfile(exepath):
1414
raise Exception('JULIAPY_EXE=%s does not exist' % repr(exepath))
15-
_CONFIG['exepath'] = exepath
15+
CONFIG['exepath'] = exepath
1616
libpath = subprocess.run([exepath, '-e', 'import Libdl; print(abspath(Libdl.dlpath("libjulia")))'], stdout=(subprocess.PIPE)).stdout.decode('utf8')
1717
else:
1818
if not os.path.isfile(libpath):
1919
raise Exception('JULIAPY_LIB=%s does not exist' % repr(libpath))
20-
_CONFIG['libpath'] = libpath
20+
CONFIG['libpath'] = libpath
2121
try:
2222
d = os.getcwd()
2323
os.chdir(os.path.dirname(libpath))
2424
lib = c.CDLL(libpath)
2525
finally:
2626
os.chdir(d)
2727

28-
_CONFIG['lib'] = lib
28+
CONFIG['lib'] = lib
2929
lib.jl_init__threading.argtypes = []
3030
lib.jl_init__threading.restype = None
3131
lib.jl_init__threading()
@@ -37,7 +37,7 @@ def _init_():
3737
ENV["PYTHONJL_LIBPTR"] = "{}"
3838
import Python
3939
Python.with_gil() do
40-
Python.pyimport("sys").modules["julia"].Main = Python.pyjlraw(Main)
40+
Python.pyimport("sys").modules["julia"].Main = Python.pyjl(Main)
4141
end
4242
catch err
4343
@error "Error loading Python.jl" err=err
@@ -47,22 +47,12 @@ def _init_():
4747
if res is None:
4848
raise Exception('Python.jl did not start properly. Ensure that the Python package is installed in Julia.')
4949

50-
class Wrapper(types.ModuleType):
51-
52-
def __getattr__(self, k):
53-
return getattr(self.Main, k)
54-
55-
def __dir__(self):
56-
return super().__dir__() + self.Main.__dir__()
57-
58-
sys.modules['julia'].__class__ = Wrapper
59-
60-
_init_()
61-
del _init_
50+
init()
51+
del init
6252

6353
Core = Main.Core
6454
Base = Main.Base
6555
Python = Main.Python
6656

67-
def _import(*names):
68-
Main.eval(Base.Meta.parse('import ' + ', '.join(names)))
57+
def newmodule(name):
58+
return Base.Module(Base.Symbol(name))

src/cpython/juliaany.jl

Lines changed: 316 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,316 @@
1+
const PyJuliaAnyValue_Type__ref = Ref(PyPtr())
2+
PyJuliaAnyValue_Type() = begin
3+
ptr = PyJuliaAnyValue_Type__ref[]
4+
if isnull(ptr)
5+
c = []
6+
base = PyJuliaBaseValue_Type()
7+
isnull(base) && return PyPtr()
8+
t = fill(PyType_Create(c,
9+
name = "julia.AnyValue",
10+
base = base,
11+
repr = pyjlany_repr,
12+
str = pyjlany_str,
13+
getattro = pyjlany_getattro,
14+
setattro = pyjlany_setattro,
15+
call = pyjlany_call,
16+
iter = pyjlany_iter,
17+
richcompare = pyjlany_richcompare,
18+
as_mapping = (
19+
length = pyjlany_length,
20+
subscript = pyjlany_getitem,
21+
ass_subscript = pyjlany_setitem,
22+
),
23+
as_sequence = (
24+
contains = pyjlany_contains,
25+
),
26+
methods = [
27+
(name="__dir__", flags=Py_METH_NOARGS, meth=pyjlany_dir),
28+
],
29+
))
30+
ptr = PyPtr(pointer(t))
31+
err = PyType_Ready(ptr)
32+
ism1(err) && return PyPtr()
33+
PYJLGCCACHE[ptr] = push!(c, t)
34+
PyJuliaAnyValue_Type__ref[] = ptr
35+
end
36+
ptr
37+
end
38+
39+
PyJuliaAnyValue_New(x) = PyJuliaValue_New(PyJuliaAnyValue_Type(), x)
40+
PyJuliaValue_From(x) = PyJuliaAnyValue_New(x)
41+
42+
pyjlany_repr(xo::PyPtr) = try
43+
x = PyJuliaValue_GetValue(xo)
44+
s = "<jl $(repr(x))>"
45+
PyUnicode_From(s)
46+
catch err
47+
PyErr_SetJuliaError(err)
48+
PyPtr()
49+
end
50+
51+
pyjlany_str(xo::PyPtr) = try
52+
x = PyJuliaValue_GetValue(xo)
53+
s = string(x)
54+
PyUnicode_From(s)
55+
catch err
56+
PyErr_SetJuliaError(err)
57+
return PyPtr()
58+
end
59+
60+
pyjlany_getattro(xo::PyPtr, ko::PyPtr) = begin
61+
# Try generic lookup first
62+
ro = PyObject_GenericGetAttr(xo, ko)
63+
if isnull(ro) && PyErr_IsSet(PyExc_AttributeError())
64+
PyErr_Clear()
65+
else
66+
return ro
67+
end
68+
# Now try to get the corresponding property
69+
x = PyJuliaValue_GetValue(xo)
70+
k = PyUnicode_AsString(ko)
71+
isempty(k) && PyErr_IsSet() && return PyPtr()
72+
k = pyjl_attr_py2jl(k)
73+
try
74+
v = getproperty(x, Symbol(k))
75+
PyObject_From(v)
76+
catch err
77+
if (err isa UndefVarError && err.var === Symbol(k)) || (err isa ErrorException && occursin("has no field", err.msg))
78+
PyErr_SetStringFromJuliaError(PyExc_AttributeError(), err)
79+
else
80+
PyErr_SetJuliaError(err)
81+
end
82+
PyPtr()
83+
end
84+
end
85+
86+
propertytype(x, k) =
87+
propertiesarefields(typeof(x)) && hasfield(typeof(x), k) ? fieldtype(typeof(x), k) : Any
88+
@generated propertiesarefields(::Type{T}) where {T} =
89+
which(getproperty, Tuple{T,Symbol}) == which(getproperty, Tuple{Any,Symbol})
90+
91+
pyjlany_setattro(xo::PyPtr, ko::PyPtr, vo::PyPtr) = begin
92+
# Try generic lookup first
93+
ro = PyObject_GenericSetAttr(xo, ko, vo)
94+
if ism1(ro) && PyErr_IsSet(PyExc_AttributeError())
95+
PyErr_Clear()
96+
else
97+
return ro
98+
end
99+
if isnull(vo)
100+
PyErr_SetString(PyExc_TypeError(), "attribute deletion not supported")
101+
return Cint(-1)
102+
end
103+
# Now try to set the corresponding property
104+
x = PyJuliaValue_GetValue(xo)
105+
k = PyUnicode_AsString(ko)
106+
isempty(k) && PyErr_IsSet() && return Cint(-1)
107+
k = pyjl_attr_py2jl(k)
108+
try
109+
V = propertytype(x, Symbol(k))
110+
ism1(PyObject_Convert(vo, V)) && return Cint(-1)
111+
v = takeresult(V)
112+
setproperty!(x, Symbol(k), v)
113+
Cint(0)
114+
catch err
115+
if (err isa UndefVarError && err.var === Symbol(k)) || (err isa ErrorException && occursin("has no field", err.msg))
116+
PyErr_SetStringFromJuliaError(PyExc_AttributeError(), err)
117+
else
118+
PyErr_SetJuliaError(err)
119+
end
120+
Cint(-1)
121+
end
122+
end
123+
124+
pyjl_dir(x) = propertynames(x)
125+
pyjl_dir(x::Module) = begin
126+
r = Symbol[]
127+
append!(r, names(x, all=true, imported=true))
128+
for m in ccall(:jl_module_usings, Any, (Any,), x)::Vector
129+
append!(r, names(m))
130+
end
131+
r
132+
end
133+
134+
pyjlany_dir(xo::PyPtr, _::PyPtr) = begin
135+
fo = PyObject_GetAttrString(PyJuliaBaseValue_Type(), "__dir__")
136+
isnull(fo) && return PyPtr()
137+
ro = PyObject_CallNice(fo, PyObjectRef(xo))
138+
Py_DecRef(fo)
139+
isnull(ro) && return PyPtr()
140+
x = PyJuliaValue_GetValue(xo)
141+
ks = try
142+
collect(map(string, pyjl_dir(x)))
143+
catch err
144+
Py_DecRef(ro)
145+
PyErr_SetJuliaError(err)
146+
return PyPtr()
147+
end
148+
for k in ks
149+
ko = PyUnicode_From(pyjl_attr_jl2py(k))
150+
isnull(ko) && (Py_DecRef(ro); return PyPtr())
151+
err = PyList_Append(ro, ko)
152+
Py_DecRef(ko)
153+
ism1(err) && (Py_DecRef(ro); return PyPtr())
154+
end
155+
return ro
156+
end
157+
158+
pyjlany_call(fo::PyPtr, argso::PyPtr, kwargso::PyPtr) = begin
159+
f = PyJuliaValue_GetValue(fo)
160+
if isnull(argso)
161+
args = Vector{Any}()
162+
else
163+
ism1(PyObject_Convert(argso, Vector{Any})) && return PyPtr()
164+
args = takeresult(Vector{Any})
165+
end
166+
if isnull(kwargso)
167+
kwargs = Dict{Symbol, Any}()
168+
else
169+
ism1(PyObject_Convert(kwargso, Dict{Symbol, Any})) && return PyPtr()
170+
kwargs = takeresult(Dict{Symbol, Any})
171+
end
172+
try
173+
x = f(args...; kwargs...)
174+
PyObject_From(x)
175+
catch err
176+
if err isa MethodError && err.f === f
177+
PyErr_SetStringFromJuliaError(PyExc_TypeError(), err)
178+
else
179+
PyErr_SetJuliaError(err)
180+
end
181+
PyPtr()
182+
end
183+
end
184+
185+
pyjlany_length(xo::PyPtr) = try
186+
x = PyJuliaValue_GetValue(xo)
187+
Py_ssize_t(length(x))
188+
catch err
189+
if err isa MethodError && err.f === length
190+
PyErr_SetStringFromJuliaError(PyExc_TypeError(), err)
191+
else
192+
PyErr_SetJuliaError(err)
193+
end
194+
Py_ssize_t(-1)
195+
end
196+
197+
@generated pyjl_keytype(::Type{T}) where {T} = try; keytype(T); catch; nothing; end;
198+
pyjl_hasvarindices(::Type) = true
199+
pyjl_hasvarindices(::Type{<:AbstractDict}) = false
200+
201+
pyjl_getindices(x, ko) =
202+
if (K = pyjl_keytype(typeof(x))) !== nothing
203+
ism1(PyObject_Convert(ko, K)) ? PYERR() : (takeresult(K),)
204+
elseif pyjl_hasvarindices(typeof(x)) && PyTuple_Check(ko)
205+
ism1(PyObject_TryConvert(ko, Tuple)) ? PYERR() : takeresult(Tuple)
206+
else
207+
ism1(PyObject_TryConvert(ko, Any)) ? PYERR() : takeresult(Any)
208+
end
209+
210+
pyjlany_getitem(xo::PyPtr, ko::PyPtr) = begin
211+
x = PyJuliaValue_GetValue(xo)
212+
k = pyjl_getindices(x, ko)
213+
k === PYERR() && return PyPtr()
214+
try
215+
PyObject_From(x[k...])
216+
catch err
217+
if err isa BoundsError && err.a === x
218+
PyErr_SetStringFromJuliaError(PyExc_IndexError(), err)
219+
elseif err isa KeyError && (err.key === k || (err.key,) === k)
220+
PyErr_SetStringFromJuliaError(PyExc_KeyError(), err)
221+
else
222+
PyErr_SetJuliaError(err)
223+
end
224+
PyPtr()
225+
end
226+
end
227+
228+
@generated pyjl_valtype(::Type{T}) where {T} = try; valtype(T); catch; try eltype(T); catch; nothing; end; end;
229+
230+
pyjl_getvalue(x, vo) =
231+
if (V = pyjl_valtype(typeof(x))) !== nothing
232+
ism1(PyObject_Convert(vo, V)) ? PYERR() : takeresult(V)
233+
else
234+
ism1(PyObject_Convert(vo, Any)) ? PYERR() : takeresult(Any)
235+
end
236+
237+
pyjlany_setitem(xo::PyPtr, ko::PyPtr, vo::PyPtr) = begin
238+
x = PyJuliaValue_GetValue(xo)
239+
k = pyjl_getindices(x, ko)
240+
k === PYERR() && return Cint(-1)
241+
try
242+
if isnull(vo)
243+
delete!(x, k...)
244+
Cint(0)
245+
else
246+
v = pyjl_getvalue(x, vo)
247+
v === PYERR() && return Cint(-1)
248+
x[k...] = v
249+
Cint(0)
250+
end
251+
catch
252+
if err isa BoundsError && err.a === x
253+
PyErr_SetStringFromJuliaError(PyExc_IndexError(), err)
254+
elseif err isa KeyError && (err.key === k || (err.key,) === k)
255+
PyErr_SetStringFromJuliaError(PyExc_KeyError(), err)
256+
elseif err isa MethodError && err.f === delete!
257+
PyErr_SetStringFromJuliaError(PyExc_TypeError(), err)
258+
else
259+
PyErr_SetJuliaError(err)
260+
end
261+
Cint(-1)
262+
end
263+
end
264+
265+
pyjlany_iter(xo::PyPtr) = PyJuliaIteratorValue_New(Iterator(PyJuliaValue_GetValue(xo)))
266+
267+
pyjlany_contains(xo::PyPtr, vo::PyPtr) = begin
268+
x = PyJuliaValue_GetValue(xo)
269+
r = PyObject_TryConvert(vo, eltype(x))
270+
r == -1 && return Cint(-1)
271+
r == 0 && return Cint(0)
272+
v = takeresult(eltype(x))
273+
try
274+
Cint(v in x)
275+
catch err
276+
if err isa MethodError && err.f === :in
277+
PyErr_SetStringFromJuliaError(PyExc_TypeError(), err)
278+
else
279+
PyErr_SetJuliaError(err)
280+
end
281+
Cint(-1)
282+
end
283+
end
284+
285+
pyjlany_richcompare(xo::PyPtr, yo::PyPtr, op::Cint) = begin
286+
x = PyJuliaValue_GetValue(xo)
287+
r = PyObject_TryConvert(yo, Any)
288+
r == -1 && return PyPtr()
289+
r == 0 && return PyNotImplemented_New()
290+
y = takeresult()
291+
try
292+
if op == Py_EQ
293+
PyObject_From(x == y)
294+
elseif op == Py_NE
295+
PyObject_From(x != y)
296+
elseif op == Py_LE
297+
PyObject_From(x <= y)
298+
elseif op == Py_LT
299+
PyObject_From(x < y)
300+
elseif op == Py_GE
301+
PyObject_From(x >= y)
302+
elseif op == Py_GT
303+
PyObject_From(x > y)
304+
else
305+
PyErr_SetString(PyExc_ValueError(), "bad op given to richcompare: $op")
306+
PyPtr()
307+
end
308+
catch err
309+
if err isa MethodError && err.f in (==, !=, <=, <, >=, >)
310+
PyNotImplemented_New()
311+
else
312+
PyErr_SetJuliaError(err)
313+
PyPtr()
314+
end
315+
end
316+
end

0 commit comments

Comments
 (0)