Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
# Changelog

## Unreleased
* Added option `lib` to JuliaCall. Setting this will skip the discovery subprocess.
* Add option `lib` to JuliaCall. Setting this will skip the discovery subprocess.
* Add support for using a system image in `juliacall` that has `PythonCall` baked in.
* Bug fixes.

## 0.9.34 (2026-05-18)
Expand Down
18 changes: 11 additions & 7 deletions pysrc/juliacall/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -240,6 +240,11 @@ def args_from_config(config):
jl_parse_opts(c.pointer(argc), c.pointer(argv))
assert argc.value == 0

# override some environment variables
# we do this here because PythonCall is initialised during jl_init if it is in a sysimg
os.environ['JULIA_PYTHONCALL_EXECUTABLE'] = sys.executable or ''
os.environ['__JULIA_PYTHONCALL_EMBEDDED_LIBPTR__'] = hex(c.pythonapi._handle)

# initialise julia
try:
jl_init = lib.jl_init_with_image__threading
Expand Down Expand Up @@ -269,23 +274,22 @@ def jlstr(x):
return 'raw"' + x + '"'
script = '''
try
Base.require(Main, :CompilerSupportLibraries_jll)
global __PythonCall_libptr = Ptr{{Cvoid}}(UInt({}))
ENV["JULIA_PYTHONCALL_EXE"] = {}
import CompilerSupportLibraries_jll as _
using PythonCall
catch err
print(stderr, "ERROR: ")
showerror(stderr, err, catch_backtrace())
flush(stderr)
rethrow()
end
'''.format(
hex(c.pythonapi._handle),
jlstr(sys.executable or ''),
)
'''
res = jl_eval(script.encode('utf8'))
if res is None:
raise Exception('PythonCall.jl did not start properly')

# unset this env var so any other julia processes started do not think they are
# embedded in python
os.environ.pop('__JULIA_PYTHONCALL_EMBEDDED_LIBPTR__', None)

CONFIG['inited'] = True

Expand Down
7 changes: 5 additions & 2 deletions src/C/context.jl
Original file line number Diff line number Diff line change
Expand Up @@ -105,11 +105,14 @@ on_main_thread

function init_context()

CTX.is_embedded = hasproperty(Base.Main, :__PythonCall_libptr)
CTX.is_embedded = haskey(ENV, "__JULIA_PYTHONCALL_EMBEDDED_LIBPTR__")

if CTX.is_embedded
# In this case, getting a handle to libpython is easy
CTX.lib_ptr = Base.Main.__PythonCall_libptr::Ptr{Cvoid}
CTX.lib_ptr = Ptr{Cvoid}(parse(UInt, ENV["__JULIA_PYTHONCALL_EMBEDDED_LIBPTR__"]))
# Delete the env var so subprocesses don't think they are embedded
delete!(ENV, "__JULIA_PYTHONCALL_EMBEDDED_LIBPTR__")
# Initialise pointers from libpython
init_pointers()
# Check Python is initialized
Py_IsInitialized() == 0 && error("Python is not already initialized.")
Expand Down
Loading