11# This module gets modified by PythonCall when it is loaded, e.g. to include Core, Base
22# and Main modules.
33
4- __version__ = '0.9.33 '
4+ __version__ = '0.9.34 '
55
66_newmodule = None
77
@@ -170,39 +170,47 @@ def args_from_config(config):
170170 CONFIG ['opt_handle_signals' ] = choice ('handle_signals' , ['yes' , 'no' ])[0 ]
171171 CONFIG ['opt_startup_file' ] = choice ('startup_file' , ['yes' , 'no' ])[0 ]
172172 CONFIG ['opt_heap_size_hint' ] = option ('heap_size_hint' )[0 ]
173- CONFIG ['project' ] = path_option ('project' , check_exists = True )[0 ]
174- CONFIG ['exepath' ] = executable_option ('exe' )[0 ]
173+ CONFIG ['project' ] = project = path_option ('project' , check_exists = True )[0 ]
174+ CONFIG ['libpath' ] = libpath = path_option ('lib' , check_exists = True )[0 ]
175+ CONFIG ['exepath' ] = exepath = executable_option ('exe' )[0 ]
175176
176177 # Stop if we already initialised
177178 if CONFIG ['inited' ]:
178179 return
179180
180- have_exepath = CONFIG ['exepath' ] is not None
181- have_project = CONFIG ['project' ] is not None
182- if have_exepath and have_project :
181+ if (exepath is None ) and (bindir is not None ):
182+ # if bindir is set then set exepath={bindir}/julia
183+ CONFIG ['exepath' ] = exepath = os .path .join (bindir , 'julia.exe' if os .name == 'nt' else 'julia' )
184+ if (exepath is not None ) and (project is not None ):
183185 pass
184- elif (not have_exepath ) and (not have_project ):
186+ elif (exepath is None ) and (project is None ):
185187 # we don't import this at the top level because it is not required when
186188 # juliacall is loaded by PythonCall and won't be available, or if both
187189 # `exepath` and `project` are set by the user.
188190 import juliapkg
189191
190192 # Find the Julia executable and project
191- CONFIG ['exepath' ] = juliapkg .executable ()
192- CONFIG ['project' ] = juliapkg .project ()
193+ CONFIG ['exepath' ] = exepath = juliapkg .executable ()
194+ CONFIG ['project' ] = project = juliapkg .project ()
195+ if libpath is None :
196+ CONFIG ['libpath' ] = libpath = juliapkg .libjulia ()
193197 else :
194198 raise Exception ("Both PYTHON_JULIACALL_PROJECT and PYTHON_JULIACALL_EXE must be set together, not only one of them." )
195-
196- exepath = CONFIG ['exepath' ]
197- project = CONFIG ['project' ]
198-
199- # Find the Julia library
200- cmd = [exepath , '--project=' + project , '--startup-file=no' , '-O0' , '--compile=min' ,
201- '-e' , 'import Libdl; print(abspath(Libdl.dlpath("libjulia")), "\\ 0", Sys.BINDIR)' ]
202- libpath , default_bindir = subprocess .run (cmd , check = True , capture_output = True , encoding = 'utf8' ).stdout .split ('\0 ' )
199+ if (libpath is not None ) and (exepath is None ):
200+ raise Exception ("PYTHON_JULIACALL_EXE is required if PYTHON_JULIACALL_LIB is set." )
201+
202+ # Find the Julia library, if not specified.
203+ if libpath is None :
204+ cmd = [exepath , '--project=' + project , '--startup-file=no' , '-O0' , '--compile=min' ,
205+ '-e' , 'import Libdl; print(abspath(Libdl.dlpath("libjulia")), "\\ 0", Sys.BINDIR)' ]
206+ libpath , found_bindir = subprocess .run (cmd , check = True , capture_output = True , encoding = 'utf8' ).stdout .split ('\0 ' )
207+ CONFIG ['libpath' ] = libpath
208+ if bindir is None :
209+ CONFIG ['bindir' ] = bindir = found_bindir
210+ if bindir is None :
211+ bindir = os .path .dirname (exepath )
203212 assert os .path .exists (libpath )
204- assert os .path .exists (default_bindir )
205- CONFIG ['libpath' ] = libpath
213+ assert os .path .exists (bindir )
206214
207215 # Add the Julia library directory to the PATH on Windows so Julia's system libraries can
208216 # be found. They are normally found because they are in the same directory as julia.exe,
@@ -225,6 +233,11 @@ def args_from_config(config):
225233 jl_parse_opts (c .pointer (argc ), c .pointer (argv ))
226234 assert argc .value == 0
227235
236+ # override some environment variables
237+ # we do this here because PythonCall is initialised during jl_init if it is in a sysimg
238+ os .environ ['JULIA_PYTHONCALL_EXECUTABLE' ] = sys .executable or ''
239+ os .environ ['__JULIA_PYTHONCALL_EMBEDDED_LIBPTR__' ] = hex (c .pythonapi ._handle )
240+
228241 # initialise julia
229242 try :
230243 jl_init = lib .jl_init_with_image__threading
@@ -233,7 +246,7 @@ def args_from_config(config):
233246 jl_init .argtypes = [c .c_char_p , c .c_char_p ]
234247 jl_init .restype = None
235248 jl_init (
236- ( default_bindir if bindir is None else bindir ) .encode ('utf8' ),
249+ None if bindir is None else bindir .encode ('utf8' ),
237250 None if sysimg is None else sysimg .encode ('utf8' ),
238251 )
239252
@@ -254,23 +267,22 @@ def jlstr(x):
254267 return 'raw"' + x + '"'
255268 script = '''
256269 try
257- Base.require(Main, :CompilerSupportLibraries_jll)
258- global __PythonCall_libptr = Ptr{{Cvoid}}(UInt({}))
259- ENV["JULIA_PYTHONCALL_EXE"] = {}
270+ import CompilerSupportLibraries_jll as _
260271 using PythonCall
261272 catch err
262273 print(stderr, "ERROR: ")
263274 showerror(stderr, err, catch_backtrace())
264275 flush(stderr)
265276 rethrow()
266277 end
267- ''' .format (
268- hex (c .pythonapi ._handle ),
269- jlstr (sys .executable or '' ),
270- )
278+ '''
271279 res = jl_eval (script .encode ('utf8' ))
272280 if res is None :
273281 raise Exception ('PythonCall.jl did not start properly' )
282+
283+ # unset this env var so any other julia processes started do not think they are
284+ # embedded in python
285+ os .environ .pop ('__JULIA_PYTHONCALL_EMBEDDED_LIBPTR__' , None )
274286
275287 CONFIG ['inited' ] = True
276288
0 commit comments