From 2e1499f8d96d11e43d17396d329202e712a2025b Mon Sep 17 00:00:00 2001 From: Christopher Rowley Date: Thu, 4 Jun 2026 11:56:39 +0100 Subject: [PATCH 1/2] remove CONFIG, add fix_qt_plugin_path preference --- CHANGELOG.md | 3 ++- docs/src/pythoncall.md | 1 + docs/src/v1-migration-guide.md | 7 ++++++ src/API/publics.jl | 1 - src/Compat/gui.jl | 41 ++++++++-------------------------- src/Core/Core.jl | 1 - src/Core/config.jl | 6 ----- src/PythonCall.jl | 3 --- src/Utils/Utils.jl | 3 +++ 9 files changed, 22 insertions(+), 44 deletions(-) delete mode 100644 src/Core/config.jl diff --git a/CHANGELOG.md b/CHANGELOG.md index 5463422f..f121eccc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,8 @@ * Removed unsafe API: `getptr`, `pycopy!`, `pyisnull`, `pynew`, `PyNULL`, `unsafe_pynext`. A `Py` is now always a valid, fixed, non-NULL Python object in the documented API. * Python errors no longer automatically set `sys.last_traceback` etc. when displayed from Julia. - * Removed `CONFIG.auto_sys_last_traceback`. + * Added [`fix_qt_plugin_path` preference](@ref pythoncall-config), replacing `CONFIG.auto_fix_qt_plugin_path`. + * Removed `PythonCall.CONFIG`. * Changes to `PythonCall.GC` (now more like `Base.GC`): * `enable(true)` replaces `enable()`. * `enable(false)` replaces `disable()`. diff --git a/docs/src/pythoncall.md b/docs/src/pythoncall.md index 7c604b9a..c2cd2aac 100644 --- a/docs/src/pythoncall.md +++ b/docs/src/pythoncall.md @@ -289,6 +289,7 @@ variables. | `exe` | `JULIA_PYTHONCALL_EXE` | Path to the Python executable, or special values (see below). | | `lib` | `JULIA_PYTHONCALL_LIB` | Path to the Python library (usually inferred automatically). | | `pickle` | `JULIA_PYTHONCALL_PICKLE` | Pickle module to use for serialization (`pickle` or `dill`). | +| `fix_qt_plugin_path=` | `JULIA_PYTHONCALL_FIX_QT_PLUGIN_PATH=` | When true (the default), automatically [fix the Qt plugin path](@ref `PythonCall.fix_qt_plugin_path`) when activating a Qt-based event loop. | The easiest way to set these preferences is with the [`PreferenceTools`](https://github.com/cjdoris/PreferenceTools.jl) diff --git a/docs/src/v1-migration-guide.md b/docs/src/v1-migration-guide.md index cef13097..97f0aca3 100644 --- a/docs/src/v1-migration-guide.md +++ b/docs/src/v1-migration-guide.md @@ -65,6 +65,13 @@ work. * Instead of `pdb.pm()` use `pdb.post_mortem(err[1].exception)`. +The `PythonCall.CONFIG.auto_fix_qt_plugin_path` config has been replaced with the +[`fix_qt_plugin_path` preference](@ref pythoncall-config). + +* Instead of `PythonCall.CONFIG.auto_fix_qt_plugin_path = false`, set preference + `pkg> preference add PythonCall fix_qt_plugin_path=false` or the env var + `JULIA_PYTHONCALL_FIX_QT_PLUGIN_PATH=0`. + ## `PythonCall.GC` This submodule has been changed to closer mimic the `Base.GC` API. diff --git a/src/API/publics.jl b/src/API/publics.jl index a8c4cdd1..53338b27 100644 --- a/src/API/publics.jl +++ b/src/API/publics.jl @@ -12,7 +12,6 @@ if Base.VERSION ≥ v"1.11" python_version, # Core - CONFIG, pydel!, # Compat diff --git a/src/Compat/gui.jl b/src/Compat/gui.jl index b0be9363..ae4fcb4e 100644 --- a/src/Compat/gui.jl +++ b/src/Compat/gui.jl @@ -7,7 +7,7 @@ This fixes the problem that Qt does not know where to find its `qt.conf` file, b always looks relative to `sys.executable`, which can be the Julia executable not the Python one when using this package. -If `CONFIG.auto_fix_qt_plugin_path` is true, then this is run automatically before `PyQt4`, `PyQt5`, `PySide`, `PySide2` or `PySide6` are imported. +If the `fix_qt_plugin_path` preference is true, then this is run automatically before `PyQt4`, `PyQt5`, `PySide`, `PySide2` or `PySide6` are imported. """ function fix_qt_plugin_path() C.CTX.exe_path === nothing && return false @@ -53,30 +53,6 @@ function fix_qt_plugin_path() return false end -# """ -# pyinteract(; force=false, sleep=0.1) - -# Some Python GUIs can work interactively, meaning the GUI is available but the interactive prompt is returned (e.g. after calling `matplotlib.pyplot.ion()`). -# To use these from Julia, currently you must manually call `pyinteract()` each time you want to interact. - -# Internally, this is calling the `PyOS_InputHook` asynchronously. Only one copy is run at a time unless `force` is true. - -# The asynchronous task waits for `sleep` seconds before calling the hook function. -# This gives time for the next prompt to be printed and waiting for input. -# As a result, there will be a small delay before the GUI becomes interactive. -# """ -# pyinteract(; force::Bool = false, sleep::Real = 0.1) = -# if !CONFIG.inputhookrunning || force -# CONFIG.inputhookrunning = true -# @async begin -# sleep > 0 && Base.sleep(sleep) -# C.PyOS_RunInputHook() -# CONFIG.inputhookrunning = false -# end -# nothing -# end -# export pyinteract - const EVENT_LOOPS = Dict{Symbol,Base.Timer}() const new_event_loop_callback = pynew() @@ -158,13 +134,14 @@ function init_gui() pycopy!(new_event_loop_callback, g["new_event_loop_callback"]) # add a hook to automatically call fix_qt_plugin_path() - fixqthook = - Py(() -> (PythonCall.CONFIG.auto_fix_qt_plugin_path && fix_qt_plugin_path(); nothing)) - pymodulehooks.add_hook("PyQt4", fixqthook) - pymodulehooks.add_hook("PyQt5", fixqthook) - pymodulehooks.add_hook("PySide", fixqthook) - pymodulehooks.add_hook("PySide2", fixqthook) - pymodulehooks.add_hook("PySide6", fixqthook) + if getpref_fix_qt_plugin_path() + fixqthook = Py(fix_qt_plugin_path) + pymodulehooks.add_hook("PyQt4", fixqthook) + pymodulehooks.add_hook("PyQt5", fixqthook) + pymodulehooks.add_hook("PySide", fixqthook) + pymodulehooks.add_hook("PySide2", fixqthook) + pymodulehooks.add_hook("PySide6", fixqthook) + end end end diff --git a/src/Core/Core.jl b/src/Core/Core.jl index de144bf3..d4de014b 100644 --- a/src/Core/Core.jl +++ b/src/Core/Core.jl @@ -196,7 +196,6 @@ export include("Py.jl") include("err.jl") -include("config.jl") include("consts.jl") include("builtins.jl") include("stdlib.jl") diff --git a/src/Core/config.jl b/src/Core/config.jl deleted file mode 100644 index 967bd33e..00000000 --- a/src/Core/config.jl +++ /dev/null @@ -1,6 +0,0 @@ -@kwdef mutable struct Config - meta::String = "" - auto_fix_qt_plugin_path::Bool = true -end - -const CONFIG = Config() diff --git a/src/PythonCall.jl b/src/PythonCall.jl index a689e43d..c6b166bd 100644 --- a/src/PythonCall.jl +++ b/src/PythonCall.jl @@ -15,9 +15,6 @@ include("Wrap/Wrap.jl") include("JlWrap/JlWrap.jl") include("Compat/Compat.jl") -# non-exported API -using .Core: CONFIG - # not API but used in tests for k in [ :pyjlanytype, diff --git a/src/Utils/Utils.jl b/src/Utils/Utils.jl index a26b3198..ea10a115 100644 --- a/src/Utils/Utils.jl +++ b/src/Utils/Utils.jl @@ -12,11 +12,14 @@ end checkpref(::Type{String}, x) = error("invalid preference of type $(type(x)), expecting a string") checkpref(::Type{String}, x::AbstractString) = convert(String, x) +checkpref(::Type{Bool}, x::Bool) = x +checkpref(::Type{Bool}, x::AbstractString) = x in ("1", "yes", "true") ? true : x in ("0", "no", "false") ? false : error("expecting '0', 'no', 'false', '1', 'yes' or 'true'") # Specific preference functions getpref_exe() = getpref(String, "exe", "JULIA_PYTHONCALL_EXE", "") getpref_lib() = getpref(String, "lib", "JULIA_PYTHONCALL_LIB", nothing) getpref_pickle() = getpref(String, "pickle", "JULIA_PYTHONCALL_PICKLE", "pickle") +getpref_fix_qt_plugin_path() = getpref(Bool, "fix_qt_plugin_path", "JULIA_PYTHONCALL_FIX_QT_PLUGIN_PATH", true) function explode_union(T) @nospecialize T From 6dbb31d573bdf343e52b616479bfceb8adec1807 Mon Sep 17 00:00:00 2001 From: Christopher Rowley Date: Thu, 4 Jun 2026 12:18:42 +0100 Subject: [PATCH 2/2] fix symbol not defined --- src/Compat/gui.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Compat/gui.jl b/src/Compat/gui.jl index ae4fcb4e..ed8957b5 100644 --- a/src/Compat/gui.jl +++ b/src/Compat/gui.jl @@ -134,7 +134,7 @@ function init_gui() pycopy!(new_event_loop_callback, g["new_event_loop_callback"]) # add a hook to automatically call fix_qt_plugin_path() - if getpref_fix_qt_plugin_path() + if Utils.getpref_fix_qt_plugin_path() fixqthook = Py(fix_qt_plugin_path) pymodulehooks.add_hook("PyQt4", fixqthook) pymodulehooks.add_hook("PyQt5", fixqthook)