1- __version__ = '0.5.1'
1+ # This module gets modified by PythonCall when it is loaded, e.g. to include Core, Base
2+ # and Main modules.
23
3- CONFIG = {}
4+ __version__ = '0.5.1'
45
56def newmodule (name ):
67 "A new module with the given name."
7- from . import Base
88 return Base .Module (Base .Symbol (name ))
99
1010class As :
1111 "Interpret 'value' as type 'type' when converting to Julia."
1212 __slots__ = ("value" , "type" )
13- __module__ = "juliacall"
1413 def __init__ (self , value , type ):
1514 self .value = value
1615 self .type = type
@@ -19,24 +18,84 @@ def __repr__(self):
1918
2019class JuliaError (Exception ):
2120 "An error arising in Julia code."
22- __module__ = "juliacall"
2321 def __init__ (self , exception , stacktrace = None ):
2422 super ().__init__ (exception , stacktrace )
2523 def __str__ (self ):
2624 e = self .exception
27- if isinstance (e , str ):
28- return e
25+ b = self .stacktrace
26+ if b is None :
27+ return Base .sprint (Base .showerror , e )
2928 else :
30- from . import Base
31- io = Base .IOBuffer ()
32- Base .showerror (io , e )
33- return str (Base .String (Base .take_b (io )))
29+ return Base .sprint (Base .showerror , e , b )
3430 @property
3531 def exception (self ):
3632 return self .args [0 ]
3733 @property
3834 def stacktrace (self ):
3935 return self .args [1 ]
4036
41- from .init import init
37+ CONFIG = {'inited' : False }
38+
39+ def init ():
40+ import os
41+ import ctypes as c
42+ import sys
43+ import subprocess
44+
45+ # Determine if we should skip initialising.
46+ CONFIG ['noinit' ] = os .getenv ('PYTHON_JULIACALL_NOINIT' , '' ) == 'yes'
47+ if CONFIG ['noinit' ]:
48+ return
49+
50+ # Stop if we already initialised
51+ if CONFIG ['inited' ]:
52+ return
53+
54+ # we don't import this at the top level because it is not required when juliacall is
55+ # loaded by PythonCall and won't be available
56+ import juliapkg
57+
58+ # Find the Julia executable and project
59+ CONFIG ['exepath' ] = exepath = juliapkg .executable ()
60+ CONFIG ['project' ] = project = juliapkg .project ()
61+
62+ # Find the Julia library
63+ cmd = [exepath , '--project=' + project , '--startup-file=no' , '-O0' , '--compile=min' , '-e' , 'import Libdl; print(abspath(Libdl.dlpath("libjulia")))' ]
64+ CONFIG ['libpath' ] = libpath = subprocess .run (cmd , check = True , capture_output = True , encoding = 'utf8' ).stdout
65+ assert os .path .exists (libpath )
66+
67+ # Initialise Julia
68+ d = os .getcwd ()
69+ try :
70+ # Open the library
71+ os .chdir (os .path .dirname (libpath ))
72+ CONFIG ['lib' ] = lib = c .CDLL (libpath )
73+ lib .jl_init__threading .argtypes = []
74+ lib .jl_init__threading .restype = None
75+ lib .jl_init__threading ()
76+ lib .jl_eval_string .argtypes = [c .c_char_p ]
77+ lib .jl_eval_string .restype = c .c_void_p
78+ os .environ ['JULIA_PYTHONCALL_LIBPTR' ] = str (c .pythonapi ._handle )
79+ os .environ ['JULIA_PYTHONCALL_EXE' ] = sys .executable or ''
80+ os .environ ['JULIA_PYTHONCALL_PROJECT' ] = project
81+ script = '''
82+ try
83+ import Pkg
84+ Pkg.activate(ENV["JULIA_PYTHONCALL_PROJECT"], io=devnull)
85+ import PythonCall
86+ catch err
87+ print(stderr, "ERROR: ")
88+ showerror(stderr, err, catch_backtrace())
89+ flush(stderr)
90+ rethrow()
91+ end
92+ '''
93+ res = lib .jl_eval_string (script .encode ('utf8' ))
94+ if res is None :
95+ raise Exception ('PythonCall.jl did not start properly' )
96+ finally :
97+ os .chdir (d )
98+
99+ CONFIG ['inited' ] = True
100+
42101init ()
0 commit comments