From 9fc2c6c3cf75e4ad6f9ddd8a09afe71a1c3d413e Mon Sep 17 00:00:00 2001 From: Jonas Rembser Date: Tue, 27 May 2025 12:09:19 +0200 Subject: [PATCH 1/2] [Python] Only use the ROOT gui event loop in TerminalInteractiveShell Only the TerminalInteractiveShell will use the input hooks that are registered via terminal.pt_inputhooks, so for other shells the `%gui ROOT` magic command will not do anything anyway. It's better to not run it then, in order to avoid errors about the `ROOT` gui not being available. --- .../pythonizations/python/ROOT/_application.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/bindings/pyroot/pythonizations/python/ROOT/_application.py b/bindings/pyroot/pythonizations/python/ROOT/_application.py index a0b5655111eea..3dc3d0da9ac87 100644 --- a/bindings/pyroot/pythonizations/python/ROOT/_application.py +++ b/bindings/pyroot/pythonizations/python/ROOT/_application.py @@ -35,6 +35,7 @@ def _ipython_config(): from IPython import get_ipython from IPython.terminal import pt_inputhooks + from IPython.terminal.interactiveshell import TerminalInteractiveShell def inputhook(context): while not context.input_is_ready(): @@ -44,7 +45,10 @@ def inputhook(context): pt_inputhooks.register('ROOT', inputhook) ipy = get_ipython() - if ipy: + + # Only the TerminalInteractiveShell will use the input hooks that are + # registered via terminal.pt_inputhooks. + if ipy and isinstance(ipy, TerminalInteractiveShell): get_ipython().run_line_magic('gui', 'ROOT') @staticmethod @@ -75,10 +79,11 @@ def init_graphics(self): # Note that we only end up in this function if gROOT.IsBatch() is false import __main__ - if self._is_ipython and 'IPython' in sys.modules and sys.modules['IPython'].version_info[0] >= 5: + + if self._is_ipython and "IPython" in sys.modules and sys.modules["IPython"].version_info[0] >= 5: # ipython and notebooks, register our event processing with their hooks self._ipython_config() - elif sys.flags.interactive == 1 or not hasattr(__main__, '__file__') or gSystem.InheritsFrom('TMacOSXSystem'): + elif sys.flags.interactive == 1 or not hasattr(__main__, "__file__") or gSystem.InheritsFrom("TMacOSXSystem"): # Python in interactive mode, use the PyOS_InputHook to call our event processing # - sys.flags.interactive checks for the -i flags passed to python # - __main__ does not have the attribute __file__ if the Python prompt is started directly @@ -94,12 +99,13 @@ def _process_root_events(self): while self.keep_polling: gSystem.ProcessEvents() time.sleep(0.01) + import threading - self.keep_polling = True # Used to shut down the thread safely at teardown time + + self.keep_polling = True # Used to shut down the thread safely at teardown time update_thread = threading.Thread(None, _process_root_events, None, (self,)) - self.process_root_events = update_thread # The thread is joined at teardown time + self.process_root_events = update_thread # The thread is joined at teardown time update_thread.daemon = True update_thread.start() self._set_display_hook() - From 6162878f2214ab5f83ac3223d78e1f0a0fcc7550 Mon Sep 17 00:00:00 2001 From: Jonas Rembser Date: Tue, 23 Apr 2024 01:01:57 +0200 Subject: [PATCH 2/2] [PyROOT] Move CPython extensions into subdirectories Closes #14917. --- .../python/JupyROOT/helpers/handlers.py | 2 +- bindings/pyroot/pythonizations/CMakeLists.txt | 25 ++++++++++++++----- .../pythonizations/python/ROOT/__init__.py | 21 +++++++++++----- .../python/ROOT/_application.py | 2 +- .../pythonizations/python/ROOT/_facade.py | 2 +- .../ROOT/_pythonization/_cppinstance.py | 2 +- .../python/ROOT/_pythonization/_generic.py | 2 +- .../ROOT/_pythonization/_rdf_namespace.py | 2 +- .../python/ROOT/_pythonization/_tclass.py | 2 +- .../python/ROOT/_pythonization/_tobject.py | 2 +- .../python/ROOT/_pythonization/_ttree.py | 2 +- 11 files changed, 43 insertions(+), 21 deletions(-) diff --git a/bindings/jupyroot/python/JupyROOT/helpers/handlers.py b/bindings/jupyroot/python/JupyROOT/helpers/handlers.py index 56574839e05e8..433126fea3d95 100644 --- a/bindings/jupyroot/python/JupyROOT/helpers/handlers.py +++ b/bindings/jupyroot/python/JupyROOT/helpers/handlers.py @@ -19,7 +19,7 @@ import queue from JupyROOT import helpers -import libROOTPythonizations as _lib +import ROOT.libROOTPythonizations as _lib class IOHandler(object): diff --git a/bindings/pyroot/pythonizations/CMakeLists.txt b/bindings/pyroot/pythonizations/CMakeLists.txt index e222e04c0ef99..0c06eee0e4d4c 100644 --- a/bindings/pyroot/pythonizations/CMakeLists.txt +++ b/bindings/pyroot/pythonizations/CMakeLists.txt @@ -169,6 +169,19 @@ set(libname ROOTPythonizations) add_library(${libname} SHARED ${cpp_sources}) +# To make sure that the library also ends up in the right subdirectory in the +# build directory tree. +if(MSVC) + set_target_properties(${libname} + PROPERTIES + RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/bin/ROOT + RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/bin/ROOT + RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL ${CMAKE_BINARY_DIR}/bin/ROOT + RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO ${CMAKE_BINARY_DIR}/bin/ROOT) +else() + set_target_properties(${libname} PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib/ROOT) +endif() + # Insert the ROOTPythonizationsPySources in the dependency graph add_dependencies(${libname} ROOTPythonizationsPySources) @@ -212,16 +225,16 @@ target_link_libraries(PyROOT INTERFACE cppyy_backend cppyy ROOTPythonizations) # Install library install(TARGETS ${libname} EXPORT ${CMAKE_PROJECT_NAME}Exports - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT libraries - LIBRARY DESTINATION ${CMAKE_INSTALL_PYTHONDIR} COMPONENT libraries - ARCHIVE DESTINATION ${CMAKE_INSTALL_PYTHONDIR} COMPONENT libraries) + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}/ROOT COMPONENT libraries + LIBRARY DESTINATION ${CMAKE_INSTALL_PYTHONDIR}/ROOT COMPONENT libraries + ARCHIVE DESTINATION ${CMAKE_INSTALL_PYTHONDIR}/ROOT COMPONENT libraries) # Install meta-target PyROOT3 (INTERFACE library) # Install library install(TARGETS PyROOT EXPORT ${CMAKE_PROJECT_NAME}Exports - RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} COMPONENT libraries - LIBRARY DESTINATION ${CMAKE_INSTALL_PYTHONDIR} COMPONENT libraries - ARCHIVE DESTINATION ${CMAKE_INSTALL_PYTHONDIR} COMPONENT libraries) + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}/ROOT COMPONENT libraries + LIBRARY DESTINATION ${CMAKE_INSTALL_PYTHONDIR}/ROOT COMPONENT libraries + ARCHIVE DESTINATION ${CMAKE_INSTALL_PYTHONDIR}/ROOT COMPONENT libraries) # Install Python sources and bytecode install(DIRECTORY ${localruntimedir}/ROOT diff --git a/bindings/pyroot/pythonizations/python/ROOT/__init__.py b/bindings/pyroot/pythonizations/python/ROOT/__init__.py index b8b0e04dfa7ad..4f93a18a18624 100644 --- a/bindings/pyroot/pythonizations/python/ROOT/__init__.py +++ b/bindings/pyroot/pythonizations/python/ROOT/__init__.py @@ -8,23 +8,32 @@ # For the list of contributors see $ROOTSYS/README/CREDITS. # ################################################################################ -from os import environ +import importlib +import os +import sys # Prevent cppyy's check for the PCH -environ["CLING_STANDARD_PCH"] = "none" +os.environ["CLING_STANDARD_PCH"] = "none" # Prevent cppyy's check for extra header directory -environ["CPPYY_API_PATH"] = "none" +os.environ["CPPYY_API_PATH"] = "none" # Prevent cppyy from filtering ROOT libraries -environ["CPPYY_NO_ROOT_FILTER"] = "1" +os.environ["CPPYY_NO_ROOT_FILTER"] = "1" + +# The libROOTPythonizations CPython extension is in the same directory as the +# ROOT Python module, but to find the other ROOT libraries we need to also add +# the path of the ROOT library directory (only needed on Windows). For example, +# if the ROOT Python module is in $ROOTSYS/bin/ROOT/__init__.py, the libraries +# are usually in $ROOTSYS/bin. +if 'win32' in sys.platform: + root_module_path = os.path.dirname(__file__) # expected to be $ROOTSYS/bin/ROOT + os.add_dll_directory(os.path.dirname(root_module_path)) # expected to be $ROOTSYS/bin # Do setup specific to AddressSanitizer environments from . import _asan import cppyy -import sys, importlib -import libROOTPythonizations # Build cache of commonly used python strings (the cache is python intern, so # all strings are shared python-wide, not just in PyROOT). diff --git a/bindings/pyroot/pythonizations/python/ROOT/_application.py b/bindings/pyroot/pythonizations/python/ROOT/_application.py index 3dc3d0da9ac87..e0668863a4fc2 100644 --- a/bindings/pyroot/pythonizations/python/ROOT/_application.py +++ b/bindings/pyroot/pythonizations/python/ROOT/_application.py @@ -13,7 +13,7 @@ from cppyy.gbl import gSystem, gInterpreter, gEnv -from libROOTPythonizations import InitApplication, InstallGUIEventInputHook +from ROOT.libROOTPythonizations import InitApplication, InstallGUIEventInputHook class PyROOTApplication(object): diff --git a/bindings/pyroot/pythonizations/python/ROOT/_facade.py b/bindings/pyroot/pythonizations/python/ROOT/_facade.py index a96315b30b214..3218ba04ff99d 100644 --- a/bindings/pyroot/pythonizations/python/ROOT/_facade.py +++ b/bindings/pyroot/pythonizations/python/ROOT/_facade.py @@ -173,7 +173,7 @@ def _register_converters_and_executors(self): "Double32_t&": "double&", } - from libROOTPythonizations import CPyCppyyRegisterConverterAlias, CPyCppyyRegisterExecutorAlias + from ROOT.libROOTPythonizations import CPyCppyyRegisterConverterAlias, CPyCppyyRegisterExecutorAlias for name, target in converter_aliases.items(): CPyCppyyRegisterConverterAlias(name, target) diff --git a/bindings/pyroot/pythonizations/python/ROOT/_pythonization/_cppinstance.py b/bindings/pyroot/pythonizations/python/ROOT/_pythonization/_cppinstance.py index 2be2c6aa4c13c..b5775fc7b0e6a 100644 --- a/bindings/pyroot/pythonizations/python/ROOT/_pythonization/_cppinstance.py +++ b/bindings/pyroot/pythonizations/python/ROOT/_pythonization/_cppinstance.py @@ -10,7 +10,7 @@ def pythonize_cppinstance(): import cppyy - from libROOTPythonizations import AddCPPInstancePickling + from ROOT.libROOTPythonizations import AddCPPInstancePickling klass = cppyy._backend.CPPInstance diff --git a/bindings/pyroot/pythonizations/python/ROOT/_pythonization/_generic.py b/bindings/pyroot/pythonizations/python/ROOT/_pythonization/_generic.py index ea3bee21d6ba1..86f4eee7d9a2c 100644 --- a/bindings/pyroot/pythonizations/python/ROOT/_pythonization/_generic.py +++ b/bindings/pyroot/pythonizations/python/ROOT/_pythonization/_generic.py @@ -8,7 +8,7 @@ # For the list of contributors see $ROOTSYS/README/CREDITS. # ################################################################################ -from libROOTPythonizations import AddPrettyPrintingPyz +from ROOT.libROOTPythonizations import AddPrettyPrintingPyz def _add_getitem_checked(klass): # Parameters: diff --git a/bindings/pyroot/pythonizations/python/ROOT/_pythonization/_rdf_namespace.py b/bindings/pyroot/pythonizations/python/ROOT/_pythonization/_rdf_namespace.py index 7af4e26b732f7..73ad4e5f94ea9 100644 --- a/bindings/pyroot/pythonizations/python/ROOT/_pythonization/_rdf_namespace.py +++ b/bindings/pyroot/pythonizations/python/ROOT/_pythonization/_rdf_namespace.py @@ -94,7 +94,7 @@ def _rdataframe(local_rdf, distributed_rdf): def rdataframe(*args, **kwargs): import ROOT - from libROOTPythonizations import PyObjRefCounterAsStdAny + from ROOT.libROOTPythonizations import PyObjRefCounterAsStdAny if kwargs.get("executor", None) is not None: rdf = distributed_rdf(*args, **kwargs) diff --git a/bindings/pyroot/pythonizations/python/ROOT/_pythonization/_tclass.py b/bindings/pyroot/pythonizations/python/ROOT/_pythonization/_tclass.py index 49b1664fc3ea7..eceaecb577aff 100644 --- a/bindings/pyroot/pythonizations/python/ROOT/_pythonization/_tclass.py +++ b/bindings/pyroot/pythonizations/python/ROOT/_pythonization/_tclass.py @@ -9,7 +9,7 @@ ################################################################################ import cppyy -from libROOTPythonizations import AddTClassDynamicCastPyz +from ROOT.libROOTPythonizations import AddTClassDynamicCastPyz def pythonize_tclass(): diff --git a/bindings/pyroot/pythonizations/python/ROOT/_pythonization/_tobject.py b/bindings/pyroot/pythonizations/python/ROOT/_pythonization/_tobject.py index 762bbade14ef4..1b2e9b570234e 100644 --- a/bindings/pyroot/pythonizations/python/ROOT/_pythonization/_tobject.py +++ b/bindings/pyroot/pythonizations/python/ROOT/_pythonization/_tobject.py @@ -8,7 +8,7 @@ # For the list of contributors see $ROOTSYS/README/CREDITS. # ################################################################################ -from libROOTPythonizations import AddTObjectEqNePyz +from ROOT.libROOTPythonizations import AddTObjectEqNePyz import cppyy # Searching diff --git a/bindings/pyroot/pythonizations/python/ROOT/_pythonization/_ttree.py b/bindings/pyroot/pythonizations/python/ROOT/_pythonization/_ttree.py index 4f7dcb12e4050..23dbecdc8b15a 100644 --- a/bindings/pyroot/pythonizations/python/ROOT/_pythonization/_ttree.py +++ b/bindings/pyroot/pythonizations/python/ROOT/_pythonization/_ttree.py @@ -159,7 +159,7 @@ \endpythondoc """ -from libROOTPythonizations import GetBranchAttr, BranchPyz +from ROOT.libROOTPythonizations import GetBranchAttr, BranchPyz from ._rvec import _array_interface_dtype_map, _get_cpp_type_from_numpy_type from . import pythonization from ROOT._pythonization._memory_utils import _should_give_up_ownership, _constructor_releasing_ownership, _SetDirectory_SetOwnership