1+ import base64
2+ import io
13import os
24import sys
35
46from . import newmodule , Base
5- from importlib .machinery import ModuleSpec
7+ from importlib .machinery import ModuleSpec , SourceFileLoader
68
79class Finder :
10+ def __init__ (self , jlext = '.jl' , pyext = '.py' ):
11+ self .jlext = jlext
12+ self .pyext = pyext
13+
814 def find_spec (self , fullname , path , target = None ):
915 if path is None :
1016 path = sys .path
@@ -14,28 +20,54 @@ def find_spec(self, fullname, path, target=None):
1420 else :
1521 name = fullname .split ('.' )[- 1 ]
1622 for root in path :
17- origin = os .path .join (root , name + '.py.jl' )
18- if os .path .isfile (origin ):
19- origin = os .path .realpath (origin )
20- return ModuleSpec (fullname , Loader (), origin = origin )
21-
22- class Loader :
23- def create_module (self , spec ):
24- return None
25-
26- def exec_module (self , module ):
27- spec = module .__spec__
28- name = spec .name
29- m = module .__jl_module__ = newmodule (name )
30- with open (spec .origin ) as fp :
31- src = fp .read ()
32- m .seval ("begin;]\n " + src + "\n end" )
33- ks = [str (k ) for k in Base .names (m )]
34- ks = [k for k in ks if k != name ]
35- if not ks :
36- ks = [str (k ) for k in Base .names (m , all = True )]
37- ks = [k for k in ks if not (k == name or k .startswith ('_' ) or '#' in k )]
38- for k in ks :
39- module .__dict__ [k ] = getattr (m , k )
40-
41- sys .meta_path .append (Finder ())
23+ jlfile = os .path .join (root , name + self .jlext )
24+ if os .path .isfile (jlfile ):
25+ jlfile = os .path .realpath (jlfile )
26+ pyfile = os .path .join (root , name + self .pyext )
27+ gen_file (jlfile , pyfile )
28+ return ModuleSpec (fullname , SourceFileLoader (fullname , pyfile ), origin = jlfile )
29+
30+ def install (** kw ):
31+ finder = Finder (** kw )
32+ sys .meta_path .insert (0 , finder )
33+ return finder
34+
35+ def uninstall (finder ):
36+ sys .meta_path .remove (finder )
37+
38+ def gen_code (jl ):
39+ jlb = base64 .b64encode (jl .encode ('utf8' )).decode ('ascii' )
40+ buf = io .StringIO ()
41+ pr = lambda x : print (x , file = buf )
42+ pr ('# This file was automatically generated by juliacall.importer' )
43+ pr ('import juliacall.importer' )
44+ pr ('juliacall.importer.exec_module(__name__, """' )
45+ for i in range (0 , len (jlb ), 80 ):
46+ pr (jlb [i :i + 80 ])
47+ pr ('""")' )
48+ return buf .getvalue ()
49+
50+ def gen_file (jl , py ):
51+ with open (jl ) as fp :
52+ jlcode = fp .read ()
53+ pycode = gen_code (jlcode )
54+ with open (py , 'w' ) as fp :
55+ fp .write (pycode )
56+
57+ def exec_module (name , code ):
58+ pymod = sys .modules [name ]
59+ code = base64 .b64decode (code .encode ('ascii' )).decode ('utf8' )
60+ jlmod = newmodule (name )
61+ jlmod .seval ('begin\n ' + code + '\n end' )
62+ delattr (pymod , 'juliacall' )
63+ setattr (pymod , '__jl_code__' , code )
64+ setattr (pymod , '__jl_module__' , jlmod )
65+ ks = [str (k ) for k in Base .names (jlmod )]
66+ ks = [k for k in ks if k != name ]
67+ if not ks :
68+ ks = [str (k ) for k in Base .names (jlmod , all = True )]
69+ ks = [k for k in ks if not (k == name or k == 'include' or k == 'eval' or k .startswith ('_' ) or '#' in k )]
70+ setattr (pymod , '__all__' , ks )
71+ setattr (pymod , '__doc__' , str (Base .Docs .doc (jlmod )))
72+ for k in ks :
73+ setattr (pymod , k , getattr (jlmod , k ))
0 commit comments