From cf9c18693054d3b416ea8bba65f6aada0464792e Mon Sep 17 00:00:00 2001 From: Christopher Rowley Date: Sat, 30 May 2026 14:28:17 +0100 Subject: [PATCH 1/2] load python symbols globally when embedded rather than using lib_ptr (which is now NULL in this case) --- pysrc/juliacall/__init__.py | 5 ++--- src/C/context.jl | 8 ++++---- src/C/pointers.jl | 21 ++++++++++++++++----- 3 files changed, 22 insertions(+), 12 deletions(-) diff --git a/pysrc/juliacall/__init__.py b/pysrc/juliacall/__init__.py index 33991fae..2f7ff5fa 100644 --- a/pysrc/juliacall/__init__.py +++ b/pysrc/juliacall/__init__.py @@ -269,8 +269,8 @@ def jlstr(x): return 'raw"' + x + '"' script = ''' try - Base.require(Main, :CompilerSupportLibraries_jll) - global __PythonCall_libptr = Ptr{{Cvoid}}(UInt({})) + import CompilerSupportLibraries_jll as _ + global __PythonCall_embedded__ = nothing ENV["JULIA_PYTHONCALL_EXE"] = {} using PythonCall catch err @@ -280,7 +280,6 @@ def jlstr(x): rethrow() end '''.format( - hex(c.pythonapi._handle), jlstr(sys.executable or ''), ) res = jl_eval(script.encode('utf8')) diff --git a/src/C/context.jl b/src/C/context.jl index 0a1c9eb6..6cb6330e 100644 --- a/src/C/context.jl +++ b/src/C/context.jl @@ -105,12 +105,12 @@ on_main_thread function init_context() - CTX.is_embedded = hasproperty(Base.Main, :__PythonCall_libptr) + CTX.is_embedded = hasproperty(Base.Main, :__PythonCall_embedded__) if CTX.is_embedded # In this case, getting a handle to libpython is easy - CTX.lib_ptr = Base.Main.__PythonCall_libptr::Ptr{Cvoid} - init_pointers() + CTX.lib_ptr = C_NULL + init_pointers(true) # Check Python is initialized Py_IsInitialized() == 0 && error("Python is not already initialized.") CTX.is_initialized = true @@ -236,7 +236,7 @@ function init_context() end # Get function pointers from the library - init_pointers() + init_pointers(false) # Initialize the interpreter CTX.is_preinitialized = Py_IsInitialized() != 0 diff --git a/src/C/pointers.jl b/src/C/pointers.jl index 9644329f..4e0da2ad 100644 --- a/src/C/pointers.jl +++ b/src/C/pointers.jl @@ -282,19 +282,30 @@ end const POINTERS = CAPIPointers() -@eval init_pointers(p::CAPIPointers = POINTERS, lib::Ptr = CTX.lib_ptr) = begin +function _dlsym(lib::Ptr{Cvoid}, sym::Symbol) + if lib == C_NULL + # embedded case, python symbols available globally + cglobal(sym)::Ptr{Cvoid} + else + dlsym(lib, sym)::Ptr{Cvoid} + end +end + +@eval function init_pointers(embedded::Bool, p::CAPIPointers = POINTERS, lib::Ptr = CTX.lib_ptr) + @assert (lib == C_NULL) == embedded $([ - :(p.$name = dlsym(lib, $(QuoteNode(name)))) + :(p.$name = _dlsym(lib, $(QuoteNode(name)))) for name in CAPI_FUNCS ]...) $( [ :(p.$name = - Base.unsafe_load(Ptr{PyPtr}(dlsym(lib, $(QuoteNode(name)))::Ptr))) for name in CAPI_EXCEPTIONS + Base.unsafe_load(Ptr{PyPtr}(_dlsym(lib, $(QuoteNode(name)))::Ptr))) for name in CAPI_EXCEPTIONS ]... ) - $([:(p.$name = dlsym(lib, $(QuoteNode(name)))) for name in CAPI_OBJECTS]...) - p.PyOS_InputHookPtr = dlsym(CTX.lib_ptr, :PyOS_InputHook) + $([:(p.$name = _dlsym(lib, $(QuoteNode(name)))) for name in CAPI_OBJECTS]...) + p.PyOS_InputHookPtr = _dlsym(CTX.lib_ptr, :PyOS_InputHook) + nothing end for (name, (argtypes, rettype)) in CAPI_FUNC_SIGS From ea805bddd9800eec113cd234556f42c16e664a0b Mon Sep 17 00:00:00 2001 From: Christopher Rowley Date: Sat, 30 May 2026 14:55:07 +0100 Subject: [PATCH 2/2] revert to using lib_ptr, but discover it automatically --- src/C/context.jl | 36 +++++++++++++++++++++++++++++++++--- src/C/pointers.jl | 20 +++++--------------- 2 files changed, 38 insertions(+), 18 deletions(-) diff --git a/src/C/context.jl b/src/C/context.jl index 6cb6330e..e8eba368 100644 --- a/src/C/context.jl +++ b/src/C/context.jl @@ -103,14 +103,44 @@ Execute `f()` on the main thread. on_main_thread +function find_lib_ptr() + # borrowed from PyCall.jl/src/startup.jl + @static if Sys.iswindows() + EnumProcessModules(hProcess, lphModule, cb, lpcbNeeded) = + ccall(:K32EnumProcessModules, stdcall, Bool, + (Ptr{Cvoid}, Ptr{Ptr{Cvoid}}, UInt32, Ptr{UInt32}), + hProcess, lphModule, cb, lpcbNeeded) + + lpcbneeded = Ref{UInt32}() + proc_handle = ccall(:GetCurrentProcess, stdcall, Ptr{Cvoid}, ()) + handles = Vector{Ptr{Cvoid}}(undef, 20) + if EnumProcessModules(proc_handle, handles, sizeof(handles), lpcbneeded) == 0 + resize!(handles, div(lpcbneeded[],sizeof(Ptr{Cvoid}))) + if EnumProcessModules(proc_handle, handles, sizeof(handles), lpcbneeded) == 0 + error() + end + end + for handle in handles + if ccall(:GetProcAddress, stdcall, Ptr{Cvoid}, + (Ptr{Cvoid}, Ptr{UInt8}), handle, "Py_GetVersion") != C_NULL + return handle + end + end + error("could not find libpython handle") + else + return unsafe_load(cglobal(:jl_exe_handle, Ptr{Cvoid})) + end +end + + function init_context() CTX.is_embedded = hasproperty(Base.Main, :__PythonCall_embedded__) if CTX.is_embedded # In this case, getting a handle to libpython is easy - CTX.lib_ptr = C_NULL - init_pointers(true) + CTX.lib_ptr = find_lib_ptr() + init_pointers() # Check Python is initialized Py_IsInitialized() == 0 && error("Python is not already initialized.") CTX.is_initialized = true @@ -236,7 +266,7 @@ function init_context() end # Get function pointers from the library - init_pointers(false) + init_pointers() # Initialize the interpreter CTX.is_preinitialized = Py_IsInitialized() != 0 diff --git a/src/C/pointers.jl b/src/C/pointers.jl index 4e0da2ad..4114f0a0 100644 --- a/src/C/pointers.jl +++ b/src/C/pointers.jl @@ -282,29 +282,19 @@ end const POINTERS = CAPIPointers() -function _dlsym(lib::Ptr{Cvoid}, sym::Symbol) - if lib == C_NULL - # embedded case, python symbols available globally - cglobal(sym)::Ptr{Cvoid} - else - dlsym(lib, sym)::Ptr{Cvoid} - end -end - -@eval function init_pointers(embedded::Bool, p::CAPIPointers = POINTERS, lib::Ptr = CTX.lib_ptr) - @assert (lib == C_NULL) == embedded +@eval function init_pointers(p::CAPIPointers = POINTERS, lib::Ptr = CTX.lib_ptr) $([ - :(p.$name = _dlsym(lib, $(QuoteNode(name)))) + :(p.$name = dlsym(lib, $(QuoteNode(name)))) for name in CAPI_FUNCS ]...) $( [ :(p.$name = - Base.unsafe_load(Ptr{PyPtr}(_dlsym(lib, $(QuoteNode(name)))::Ptr))) for name in CAPI_EXCEPTIONS + Base.unsafe_load(Ptr{PyPtr}(dlsym(lib, $(QuoteNode(name)))::Ptr))) for name in CAPI_EXCEPTIONS ]... ) - $([:(p.$name = _dlsym(lib, $(QuoteNode(name)))) for name in CAPI_OBJECTS]...) - p.PyOS_InputHookPtr = _dlsym(CTX.lib_ptr, :PyOS_InputHook) + $([:(p.$name = dlsym(lib, $(QuoteNode(name)))) for name in CAPI_OBJECTS]...) + p.PyOS_InputHookPtr = dlsym(CTX.lib_ptr, :PyOS_InputHook) nothing end