Skip to content

Commit fa157b1

Browse files
committed
experimenting with using meta module loader for importing julia modules/functions
1 parent 01263d1 commit fa157b1

File tree

2 files changed

+162
-19
lines changed

2 files changed

+162
-19
lines changed

julia/__init__.py

Lines changed: 82 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,86 @@
11
import sys
2-
from .core import Julia
2+
import imp
3+
import keyword
34

4-
#initialize julia interpreter
5+
from .core import Julia, JuliaModule, JuliaError
6+
7+
# initialize julia interpreter
58
julia = Julia()
69

7-
#monkeypatch julia interpreter into module load path
8-
sys.modules["julia"] = julia
10+
# monkeypatch julia interpreter into module load path
11+
#sys.modules["julia"] = julia
12+
13+
# add custom import behavior for the julia "module"
14+
class JuliaImporter(object):
15+
16+
def find_module(self, fullname, path=None):
17+
print((fullname, path))
18+
if path is None:
19+
pass
20+
if fullname.startswith("julia"):
21+
return JuliaModuleLoader()
22+
else:
23+
return None
24+
25+
26+
def ismacro(name):
27+
return name.startswith("@")
28+
29+
30+
def isoperator(name):
31+
return not name[0].isalpha()
32+
33+
34+
def isprotected(name):
35+
return name.startswith("_")
36+
37+
38+
def notascii(name):
39+
try:
40+
name.encode("ascii")
41+
return False
42+
except:
43+
return True
44+
45+
46+
def isamodule(julia_name):
47+
return julia.eval("isa({}, Module)".format(julia_name))
48+
49+
50+
class JuliaModuleLoader(object):
51+
52+
def load_module(self, fullname):
53+
juliapath = fullname.lstrip("julia.")
54+
if isamodule(juliapath):
55+
mod = sys.modules.setdefault(fullname, JuliaModule(fullname))
56+
mod.__loader__ = self
57+
names = julia.eval("names({})".format(juliapath))
58+
for name in names:
59+
if ismacro(name) or isoperator(name) or isprotected(name) or notascii(name):
60+
continue
61+
attrname = name
62+
if name.endswith("!"):
63+
attrname = name.rstrip("!") + "_b"
64+
if keyword.iskeyword(name):
65+
attrname = "jl".join(name)
66+
try:
67+
module_path = ".".join((juliapath, name))
68+
module_obj = julia.eval(module_path)
69+
is_module = julia.eval("isa({}, Module)".format(module_path))
70+
if is_module:
71+
split_path = module_path.split(".")
72+
is_base = split_path[-1] == "Base"
73+
recur_module = split_path[-1] == split_path[-2]
74+
if is_module and not is_base and not recur_module:
75+
newpath = ".".join((fullname, name))
76+
module_obj = self.load_module(newpath)
77+
is_function = julia.eval("isa({}, Function)".format(module_path))
78+
if is_function:
79+
pass
80+
setattr(mod, attrname, module_obj)
81+
except Exception:
82+
# some names cannot be imported from base
83+
pass
84+
return mod
85+
86+
sys.meta_path.append(JuliaImporter())

julia/core.py

Lines changed: 80 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,16 @@
1919
import os
2020
import sys
2121
import keyword
22+
import imp
2223

2324
from ctypes import c_void_p as void_p
2425
from ctypes import c_char_p as char_p
2526
from ctypes import py_object
2627

28+
2729
# this is python 3.3 specific
2830
from types import ModuleType, FunctionType
31+
2932
#-----------------------------------------------------------------------------
3033
# Classes and funtions
3134
#-----------------------------------------------------------------------------
@@ -40,7 +43,7 @@ class ModuleChainedDict(ChainMap, dict):
4043
# http://code.activestate.com/recipes/305268/
4144
import UserDict
4245

43-
class ModuleChainedDict(UserDict.DictMixin,dict):
46+
class ModuleChainedDict(UserDict.DictMixin, dict):
4447
"""Combine mulitiple mappings for seq lookup.
4548
For example, to emulate PYthon's normal lookup sequence:"
4649
import __builtin__
@@ -59,6 +62,34 @@ def __getitem__(self, key):
5962
raise KeyError(key)
6063

6164

65+
if python_version.major == 3:
66+
from io import StringIO
67+
68+
class JuliaOutput(list):
69+
70+
def __enter__(self):
71+
self._stdout = sys.stdout
72+
sys.stdout = self._stringio = StringIO()
73+
return self
74+
75+
def __exit__(self, *args):
76+
self.extend(self._stringio.getvalue().splitlines())
77+
sys.stdout = self._stdout
78+
else:
79+
from cStringIO import StringIO
80+
81+
class JuliaOutput(list):
82+
83+
def __enter__(self):
84+
self._stdout = sys.stdout
85+
sys.stdout = self._stringio = StringIO()
86+
return self
87+
88+
def __exit__(self, *args):
89+
self.extend(self._stringio.getvalue().splitlines())
90+
sys.stdout = self._stdout
91+
92+
6293
class MetaJuliaModule(type):
6394
def __new__(meta, name, bases, dict):
6495
mod = ModuleType(name, dict.get("__doc__"))
@@ -85,7 +116,7 @@ class JuliaError(JuliaObject, Exception):
85116
pass
86117

87118

88-
class JuliaModule(JuliaObject):
119+
class JuliaModule(ModuleType):
89120
pass
90121

91122

@@ -193,11 +224,14 @@ def __init__(self, init_julia=True):
193224
# reloads.
194225
sys._julia_runtime = api
195226
basenames = []
196-
for name in self.names(self.Base):
227+
names = self.eval("names(Base)")
228+
for name in names:
197229
if ismacro(name):
198230
continue
199231
if isoperator(name):
200232
continue
233+
if name.startswith("_"):
234+
continue
201235
if name.endswith("!"):
202236
name = name.rstrip("!") + "_b"
203237
if keyword.iskeyword(name):
@@ -237,20 +271,27 @@ def help(self, name):
237271
return None
238272
self.eval('help("{}")'.format(name))
239273

240-
def methods(self, name):
241-
"""
242-
return methods
243-
"""
244-
if name is None:
245-
return None
246-
print(self.eval("string(methods({}))".format(name)))
247-
248274
def put(self, x):
249275
pass
250276

251277
def get(self, x):
252278
pass
253279

280+
def _ismodule(self, julia_name):
281+
return self.eval("isa({}, Module)".format(julia_name))
282+
#return self.isa(pycallobj, self.Module)
283+
284+
def _isfunction(self, pycallobj):
285+
return self.isa(pycallobj, self.Function)
286+
287+
def _isimmutabletype(self, pycallobj):
288+
return (self.isa(pycallobj, self.DataType) and
289+
self.isimmutable(pycallobj))
290+
291+
def _isdatatype(self, pycallobj):
292+
return (self.isa(pycallobj, self.DataType) and not
293+
self.isimmutable(pycallobj))
294+
254295
def __getattr__(self, attr):
255296
if attr is None:
256297
return None
@@ -266,10 +307,34 @@ def __getattr__(self, attr):
266307
else:
267308
julia_name = attr
268309
try:
269-
obj = self.eval(julia_name)
270-
setattr(self, attr, obj)
271-
return obj
272-
except:
310+
#import ipdb; ipdb.set_trace()
311+
jobj = self.eval(julia_name)
312+
#if self._isfunction(jobj):
313+
# func = JuliaFunction(jobj)
314+
# pyobj = jobj
315+
#elif self._isdatatype(jobj):
316+
# pyobj = jobj
317+
#elif self._isimmutabletype(jobj):
318+
# pyobj = jobj
319+
if self._ismodule(julia_name):
320+
names = self.eval("names({})".format(julia_name))
321+
#module = JuliaModule(julia_name)
322+
module = imp.new_module(julia_name)
323+
#for name in dir(jobj):
324+
# try:
325+
# setattr(module, name, getattr(jobj, name))
326+
# except:
327+
# pass
328+
for name in names:
329+
mobj = self.eval("{}.{}".format(julia_name, name))
330+
setattr(module, name, mobj)
331+
#sys.modules["julia.{}".format(julia_name)] = module
332+
return module
333+
334+
setattr(self, attr, jobj)
335+
return jobj
336+
except Exception as err:
337+
#raise err
273338
raise AttributeError(attr)
274339

275340
def eval(self, src):

0 commit comments

Comments
 (0)