Skip to content

Commit 6aeeba0

Browse files
cjdorisncudlenco
andauthored
774 v2 (#779)
* feat(juliacall): allow supplying libjulia path / bindir to skip discovery subprocess init() starts a short-lived Julia process solely to print libjulia's path and Sys.BINDIR. In pre-built containers / system images these are static and known ahead of time; allow supplying them via the libpath / default_bindir options (PYTHON_JULIACALL_LIBPATH / PYTHON_JULIACALL_DEFAULT_BINDIR) to skip the extra process. Behaviour is unchanged unless both are set. Docs and CHANGELOG updated. * change to just one new option (lib) with sensible discovery/defaults of libpath, exepath and bindir depending on if others are set * remove 'if' that is always true --------- Co-authored-by: Nicolae Cudlenco <146981376+ncudlenco@users.noreply.github.com> Co-authored-by: Christopher Rowley <github.com/cjdoris>
1 parent b4cd8cf commit 6aeeba0

3 files changed

Lines changed: 27 additions & 19 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# Changelog
22

33
## Unreleased
4+
* Added option `lib` to JuliaCall. Setting this will skip the discovery subprocess.
45
* Bug fixes.
56

67
## 0.9.34 (2026-05-18)

docs/src/juliacall.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ be configured in two ways:
142142
| `-X juliacall-heap-size-hint=<N>` | `PYTHON_JULIACALL_HEAP_SIZE_HINT=<N>` | Hint for initial heap size in bytes. |
143143
| `-X juliacall-exe=<file>` | `PYTHON_JULIACALL_EXE=<file>` | Path to Julia binary to use (overrides JuliaPkg). |
144144
| `-X juliacall-project=<dir>` | `PYTHON_JULIACALL_PROJECT=<dir>` | Path to the Julia project to use (overrides JuliaPkg). |
145+
| `-X juliacall-lib=<file>` | `PYTHON_JULIACALL_LIB=<file>` | Path to libjulia. Set to skip the relatively slow discovery process. |
145146
| `-X juliacall-trace-compile=<stderr\|name>` | `PYTHON_JULIACALL_TRACE_COMPILE=<stderr\|name>` | Print precompile statements. |
146147
| `-X juliacall-trace-compile-timing` | `PYTHON_JULIACALL_TRACE_COMPILE_TIMING=<yes\|no>` | Include timings with precompile statements. |
147148

pysrc/juliacall/__init__.py

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -179,39 +179,45 @@ def args_from_config(config):
179179
CONFIG['opt_handle_signals'] = choice('handle_signals', ['yes', 'no'])[0]
180180
CONFIG['opt_startup_file'] = choice('startup_file', ['yes', 'no'])[0]
181181
CONFIG['opt_heap_size_hint'] = option('heap_size_hint')[0]
182-
CONFIG['project'] = path_option('project', check_exists=True)[0]
183-
CONFIG['exepath'] = executable_option('exe')[0]
182+
CONFIG['project'] = project = path_option('project', check_exists=True)[0]
183+
CONFIG['libpath'] = libpath = path_option('lib', check_exists=True)[0]
184+
CONFIG['exepath'] = exepath = executable_option('exe')[0]
184185

185186
# Stop if we already initialised
186187
if CONFIG['inited']:
187188
return
188189

189-
have_exepath = CONFIG['exepath'] is not None
190-
have_project = CONFIG['project'] is not None
191-
if have_exepath and have_project:
190+
if (exepath is None) and (bindir is not None):
191+
# if bindir is set then set exepath={bindir}/julia
192+
CONFIG['exepath'] = exepath = os.path.join(bindir, 'julia.exe' if os.name == 'nt' else 'julia')
193+
if (exepath is not None) and (project is not None):
192194
pass
193-
elif (not have_exepath) and (not have_project):
195+
elif (exepath is None) and (project is None):
194196
# we don't import this at the top level because it is not required when
195197
# juliacall is loaded by PythonCall and won't be available, or if both
196198
# `exepath` and `project` are set by the user.
197199
import juliapkg
198200

199201
# Find the Julia executable and project
200-
CONFIG['exepath'] = juliapkg.executable()
201-
CONFIG['project'] = juliapkg.project()
202+
CONFIG['exepath'] = exepath = juliapkg.executable()
203+
CONFIG['project'] = project = juliapkg.project()
202204
else:
203205
raise Exception("Both PYTHON_JULIACALL_PROJECT and PYTHON_JULIACALL_EXE must be set together, not only one of them.")
204-
205-
exepath = CONFIG['exepath']
206-
project = CONFIG['project']
207-
208-
# Find the Julia library
209-
cmd = [exepath, '--project='+project, '--startup-file=no', '-O0', '--compile=min',
210-
'-e', 'import Libdl; print(abspath(Libdl.dlpath("libjulia")), "\\0", Sys.BINDIR)']
211-
libpath, default_bindir = subprocess.run(cmd, check=True, capture_output=True, encoding='utf8').stdout.split('\0')
206+
if (libpath is not None) and (exepath is None):
207+
raise Exception("PYTHON_JULIACALL_EXE is required if PYTHON_JULIACALL_LIB is set.")
208+
209+
# Find the Julia library, if not specified.
210+
if libpath is None:
211+
cmd = [exepath, '--project='+project, '--startup-file=no', '-O0', '--compile=min',
212+
'-e', 'import Libdl; print(abspath(Libdl.dlpath("libjulia")), "\\0", Sys.BINDIR)']
213+
libpath, found_bindir = subprocess.run(cmd, check=True, capture_output=True, encoding='utf8').stdout.split('\0')
214+
CONFIG['libpath'] = libpath
215+
if bindir is None:
216+
CONFIG['bindir'] = bindir = found_bindir
217+
if bindir is None:
218+
bindir = os.path.dirname(exepath)
212219
assert os.path.exists(libpath)
213-
assert os.path.exists(default_bindir)
214-
CONFIG['libpath'] = libpath
220+
assert os.path.exists(bindir)
215221

216222
# Add the Julia library directory to the PATH on Windows so Julia's system libraries can
217223
# be found. They are normally found because they are in the same directory as julia.exe,
@@ -242,7 +248,7 @@ def args_from_config(config):
242248
jl_init.argtypes = [c.c_char_p, c.c_char_p]
243249
jl_init.restype = None
244250
jl_init(
245-
(default_bindir if bindir is None else bindir).encode('utf8'),
251+
None if bindir is None else bindir.encode('utf8'),
246252
None if sysimg is None else sysimg.encode('utf8'),
247253
)
248254

0 commit comments

Comments
 (0)