Skip to content

Commit 2a25602

Browse files
authored
Merge pull request #1199 from JuliaLang/precompilation
Improve PythonCall extension precompilation
2 parents ea8db02 + edb5998 commit 2a25602

File tree

3 files changed

+64
-27
lines changed

3 files changed

+64
-27
lines changed

.github/workflows/CI.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ jobs:
1212
env:
1313
JULIA_CONDAPKG_BACKEND: System
1414
strategy:
15+
fail-fast: false
1516
matrix:
1617
julia-version: ['1.10', '1']
1718
arch: [x64]

docs/src/_changelog.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,11 @@ Changelog](https://keepachangelog.com).
1212
### Fixed
1313
- Fixed support for 32bit systems ([#1196]).
1414

15+
### Changed
16+
- Improved precompilation for the PythonCall.jl extension, if `ipywidgets` is
17+
installed in the Python environment then it will be used to execute a simple
18+
workload ([#1199]).
19+
1520
## [v1.31.0] - 2025-10-13
1621

1722
### Added

ext/PythonCallExt.jl

Lines changed: 58 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ module PythonCallExt
99

1010
import IJulia
1111
using PythonCall
12-
12+
import PrecompileTools: @compile_workload
1313

1414
# `_repr_mimebundle_()` is a standard in the IPython ecosystem for returning all
1515
# the MIME's an object supports at once.
@@ -153,8 +153,11 @@ function manager_register_target(self, target_name, callback)
153153
target_name = pyconvert(String, target_name)
154154
comm_sym = Symbol(target_name)
155155

156-
@eval function IJulia.CommManager.register_comm(comm::IJulia.CommManager.Comm{$(QuoteNode(comm_sym))}, msg)
157-
comm.on_msg = (msg) -> callback(comm, msg)
156+
if @ccall(jl_generating_output()::Cint) == 0
157+
# Only create the method if we aren't precompiling
158+
@eval function IJulia.CommManager.register_comm(comm::IJulia.CommManager.Comm{$(QuoteNode(comm_sym))}, msg)
159+
comm.on_msg = (msg) -> callback(comm, msg)
160+
end
158161
end
159162
catch e
160163
@error "PyCommManager.register_target() failed" exception=(e, catch_backtrace())
@@ -170,38 +173,46 @@ function IJulia.init_ipython()
170173
nothing
171174
end
172175

176+
function create_pycomm()
177+
pytype("PyComm", (), [
178+
"_comm" => nothing,
179+
"comm_id" => pyproperty(; get=self -> self._comm.id),
180+
pyfunc(pycomm_init; name="__init__"),
181+
pycomm_notimplemented("publish_msg"),
182+
pycomm_notimplemented("open"),
183+
pyfunc(pycomm_close; name="close"),
184+
pyfunc(pycomm_send; name="send"),
185+
pycomm_notimplemented("on_close"),
186+
pyfunc(pycomm_on_msg; name="on_msg"),
187+
pycomm_notimplemented("handle_close"),
188+
pycomm_notimplemented("handle_msg")
189+
])
190+
end
191+
192+
function create_pycommmanager()
193+
pytype("PyCommManager", (), [
194+
pyfunc(manager_register_target; name="register_target"),
195+
pycommmanager_notimplemented("unregister_target"),
196+
pycommmanager_notimplemented("register_comm"),
197+
pycommmanager_notimplemented("unregister_comm"),
198+
pycommmanager_notimplemented("get_comm"),
199+
pycommmanager_notimplemented("comm_open"),
200+
pycommmanager_notimplemented("comm_msg"),
201+
pycommmanager_notimplemented("comm_close"),
202+
])
203+
end
204+
173205
function IJulia.init_ipywidgets()
174206
global PyComm
175207
global PyCommManager
176208

177209
IJulia.init_ipython()
178210

179211
if isnothing(PyComm)
180-
PyComm = pytype("PyComm", (), [
181-
"_comm" => nothing,
182-
"comm_id" => pyproperty(; get=self -> self._comm.id),
183-
pyfunc(pycomm_init; name="__init__"),
184-
pycomm_notimplemented("publish_msg"),
185-
pycomm_notimplemented("open"),
186-
pyfunc(pycomm_close; name="close"),
187-
pyfunc(pycomm_send; name="send"),
188-
pycomm_notimplemented("on_close"),
189-
pyfunc(pycomm_on_msg; name="on_msg"),
190-
pycomm_notimplemented("handle_close"),
191-
pycomm_notimplemented("handle_msg")
192-
])
212+
PyComm = create_pycomm()
193213
end
194214
if isnothing(PyCommManager)
195-
PyCommManager = pytype("PyCommManager", (), [
196-
pyfunc(manager_register_target; name="register_target"),
197-
pycommmanager_notimplemented("unregister_target"),
198-
pycommmanager_notimplemented("register_comm"),
199-
pycommmanager_notimplemented("unregister_comm"),
200-
pycommmanager_notimplemented("get_comm"),
201-
pycommmanager_notimplemented("comm_open"),
202-
pycommmanager_notimplemented("comm_msg"),
203-
pycommmanager_notimplemented("comm_close"),
204-
])
215+
PyCommManager = create_pycommmanager()
205216
end
206217

207218
comm = pyimport("comm")
@@ -214,7 +225,7 @@ function IJulia.init_ipywidgets()
214225
nothing
215226
end
216227

217-
function IJulia.init_matplotlib(backend="ipympl")
228+
function IJulia.init_matplotlib(backend::String="ipympl")
218229
IJulia.init_ipywidgets()
219230

220231
# Make sure it's in interactive mode and it's using the backend
@@ -241,4 +252,24 @@ precompile(pycomm_on_msg, (Py, Py))
241252
precompile(pycomm_send, (Py, Py, Py, Py))
242253
precompile(pycomm_close, (Py,))
243254

255+
@compile_workload begin
256+
create_pycomm()
257+
create_pycommmanager()
258+
259+
# If ipywidgets is installed in the environment try to precompile its
260+
# initializer. This is useful because the `ipywigets.register_comm_target()`
261+
# line is pretty heavy.
262+
try
263+
pyimport("ipywidgets")
264+
IJulia.init_ipywidgets()
265+
catch ex
266+
if !(ex isa PyException)
267+
@error "Ipywidgets precompilation failed" exception=(ex, catch_backtrace())
268+
end
269+
finally
270+
global PyComm = nothing
271+
global PyCommManager = nothing
272+
end
273+
end
274+
244275
end

0 commit comments

Comments
 (0)