From 03648249280d42fccd3aef27cde186a8d0e0ce6e Mon Sep 17 00:00:00 2001 From: Antoine Lambert Date: Tue, 31 Dec 2024 11:44:49 +0100 Subject: [PATCH] talipot-python: Create a virtualenv for the embedded interpreter (WIP) --- .github/workflows/windows-mingw64-build.yml | 2 +- .../include/talipot/PythonInterpreter.h | 3 +- .../talipot-python/src/PythonInterpreter.cpp | 60 ++++++++++++++++++- software/talipot/CMakeLists.txt | 11 ++++ 4 files changed, 71 insertions(+), 5 deletions(-) diff --git a/.github/workflows/windows-mingw64-build.yml b/.github/workflows/windows-mingw64-build.yml index 21323e1c1b..d2fc7ff185 100644 --- a/.github/workflows/windows-mingw64-build.yml +++ b/.github/workflows/windows-mingw64-build.yml @@ -75,7 +75,7 @@ jobs: -DCMAKE_BUILD_TYPE=Release -DCMAKE_NEED_RESPONSE=ON -DCMAKE_INSTALL_PREFIX=$PWD/install - -DPython3_EXECUTABLE=/${{ matrix.config.msystem }}/bin/python3 + -DPython3_EXECUTABLE=/${{ matrix.config.msystem }}/bin/python.exe -DTALIPOT_BUILD_TESTS=ON -DTALIPOT_USE_CCACHE=ON .. - name: Talipot build diff --git a/library/talipot-python/include/talipot/PythonInterpreter.h b/library/talipot-python/include/talipot/PythonInterpreter.h index fe93fba14c..cdd12171ca 100644 --- a/library/talipot-python/include/talipot/PythonInterpreter.h +++ b/library/talipot-python/include/talipot/PythonInterpreter.h @@ -1,6 +1,6 @@ /** * - * Copyright (C) 2019-2021 The Talipot developers + * Copyright (C) 2019-2024 The Talipot developers * * Talipot is a fork of Tulip, created by David Auber * and the Tulip development Team from LaBRI, University of Bordeaux @@ -51,6 +51,7 @@ class TLP_PYTHON_SCOPE PythonInterpreter : public QObject, public Singleton #endif +#include #include #include @@ -133,8 +134,10 @@ int tracefunc(PyObject *, PyFrameObject *, int what, PyObject *) { const QString PythonInterpreter::pythonPluginsPath(tlpStringToQString(tlp::TalipotLibDir) + "talipot/python/"); -const QString PythonInterpreter::pythonPluginsPathHome(QDir::homePath() + "/.Talipot-" + - TALIPOT_MM_VERSION + "/plugins/python"); +const QString talipotUserDirectory = QDir::homePath() + "/.Talipot-" + TALIPOT_MM_VERSION; +const QString talipotVenvDirectory = + talipotUserDirectory + "/venv" + PythonVersionChecker::compiledVersion(); +const QString PythonInterpreter::pythonPluginsPathHome(talipotUserDirectory + "/plugins/python"); const char PythonInterpreter::pythonReservedCharacters[] = { '#', '%', '/', '+', '-', '&', '*', '<', '>', '|', '~', '^', '=', @@ -211,6 +214,8 @@ PythonInterpreter::PythonInterpreter() #endif } + setupVirtualEnv(); + holdGIL(); importModule("sys"); @@ -305,6 +310,55 @@ PythonInterpreter::PythonInterpreter() } releaseGIL(); + + setupVirtualEnv(); +} + +void PythonInterpreter::setupVirtualEnv() { +#ifdef Q_OS_WIN + if (!QFileInfo(talipotVenvDirectory + "/Scripts/pip.exe").exists()) { +#else + if (!QFileInfo(talipotVenvDirectory + "/bin/pip").exists()) { +#endif + runString(QString(R"( +import os +import platform +import sys +import venv +python_command = 'python3' +if platform.system() == 'Windows': + python_command = 'python.exe' +sys._base_executable = os.path.join('%1', python_command) +venv.create('%2', with_pip=True) +)") + .arg(QApplication::applicationDirPath(), talipotVenvDirectory)); + } + + runString(QString(R"( +import os +import platform +import sys + +base = '%1' +if platform.system() == 'Windows': + site_packages = os.path.join(base, 'Lib', 'site-packages') +else: + site_packages = os.path.join( + base, 'lib', + 'python%s.%s' % (sys.version_info.major, sys.version_info.minor), + 'site-packages') +prev_sys_path = list(sys.path) +import site +site.addsitedir(site_packages) +sys.real_prefix = sys.prefix +sys.prefix = base +new_sys_path = [] +for item in list(sys.path): + if item not in prev_sys_path: + new_sys_path.append(item) + sys.path.remove(item) +sys.path[:0] = new_sys_path)") + .arg(talipotVenvDirectory)); } PythonInterpreter::~PythonInterpreter() { diff --git a/software/talipot/CMakeLists.txt b/software/talipot/CMakeLists.txt index 4dd66b879d..6b2ff9c784 100644 --- a/software/talipot/CMakeLists.txt +++ b/software/talipot/CMakeLists.txt @@ -269,3 +269,14 @@ IF(LINUX) OUTPUT_QUIET ERROR_QUIET)") ENDIF(TALIPOT_LINUX_DESKTOP_REGISTRATION) ENDIF(LINUX) + +STRING(REPLACE "\\" "/" PYTHON_EXE_PATH "${PYTHON_EXECUTABLE}") + +INSTALL(CODE "FILE(COPY \"${PYTHON_EXE_PATH}\" + DESTINATION \"\${CMAKE_INSTALL_PREFIX}/bin/\" FOLLOW_SYMLINK_CHAIN)") + +IF(APPLE AND NOT EXISTS ${CMAKE_INSTALL_PREFIX}/bin/python3) + GET_FILENAME_COMPONENT(PYTHON_EXE_NAME ${PYTHON_EXECUTABLE} NAME) + FILE(CREATE_LINK ${CMAKE_INSTALL_PREFIX}/bin/${PYTHON_EXE_NAME} + ${CMAKE_INSTALL_PREFIX}/bin/python3 SYMBOLIC) +ENDIF(APPLE AND NOT EXISTS ${CMAKE_INSTALL_PREFIX}/bin/python3)