You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(embed): bake the embedded flag into the sysimage
Address review feedback from @cjdoris on #773:
> if you know that you are embedded, then you can find the libptr (by
> calling into the C-API functions, which will be globally available in
> this case) [...] if we can 'bake in' the fact that PythonCall is
> embedded into the sysimg, then we won't need any of these preferences.
> I wonder if we could simply do '@eval PythonCall _is_embedded=true' or
> something when we make the sysimg, so it's baked into PythonCall, then
> test for this variable in 'PythonCall.__init__'?
> [...] could you document the basic steps to actually create a sysimg?
> I don't think you need to go much into the why [...] nor mention
> Main.__PythonCall_libptr (which is internal). Instead, you can pretty
> much just say that you need to set the prefs [...]. But what's not
> totally obvious is how you set up a project and these prefs and use
> PackageCompiler to actually make the sysimg.
Design
------
Add a module-level 'const _is_embedded = Ref(false)' on PythonCall, flipped
at sysimage build time via PackageCompiler's 'script=' keyword (NOT
'precompile_execution_file=', which runs in a separate child process whose
state is not snapshotted). The mutated value is captured in the snapshot;
at runtime, 'PythonCall.__init__' reads it and takes the embedded path.
A 'const Ref' is preferred over a rebound non-const global so the C
submodule can 'import' the name once and read it without 'parentmodule'
indirection. Same baked-into-sysimage behaviour as the literal '@eval'
form suggested in review.
libpython is opened from the existing 'lib' preference / JULIA_PYTHONCALL_LIB
(added in 0.9.33). The PR does not introduce new preferences or
environment variables.
The interpreter's executable path is resolved via 'sys.executable' using
PyImport_ImportModule + PyObject_GetAttrString + PyUnicode_AsUTF8AndSize -
stable across all supported CPython versions and platforms.
If '_is_embedded[]' is true but 'Py_IsInitialized()' returns 0 - e.g. the
sysimage is loaded by a 'julia.exe' child of 'Base.compilecache' rather
than by juliacall - init_context resets CTX and downstream module
__init__s short-circuit. PythonCall loads as inactive instead of erroring.
Files
-----
src/PythonCall.jl: declare 'const _is_embedded = Ref(false)'.
src/C/C.jl: import _is_embedded into the C submodule.
src/C/context.jl: rewrite init_context() embedded branch; add
_embedded_program_path() reading sys.executable.
src/Core/Core.jl, src/Convert/Convert.jl, src/Wrap/Wrap.jl,
src/JlWrap/JlWrap.jl, src/JlWrap/C.jl, src/Compat/Compat.jl: guard
__init__ on CTX.is_initialized for the inactive-load case.
docs/src/juliacall.md: rewrite the 'Baking PythonCall into a system
image' section with a worked example.
CHANGELOG.md: Unreleased entry.
0 commit comments