LCOV - code coverage report
Current view: top level - gcore - gdalpython.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 234 285 82.1 %
Date: 2025-11-18 00:07:43 Functions: 9 11 81.8 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL Core
       4             :  * Purpose:  Python interface
       5             :  * Author:   Even Rouault, <even dot rouault at spatialys dot com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2017-2019, Even Rouault, <even dot rouault at spatialys dot
       9             :  *com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include "cpl_conv.h"
      15             : #include "cpl_error.h"
      16             : #include "cpl_string.h"
      17             : #include "cpl_spawn.h"
      18             : #include "gdalpython.h"
      19             : 
      20             : #include <algorithm>
      21             : #include <mutex>
      22             : #include <set>
      23             : #include <vector>
      24             : 
      25             : using namespace GDALPy;
      26             : 
      27             : typedef struct PyThreadState_t PyThreadState;
      28             : 
      29             : static PyThreadState *(*PyEval_SaveThread)(void) = nullptr;
      30             : static void (*PyEval_RestoreThread)(PyThreadState *) = nullptr;
      31             : static void (*Py_Finalize)(void) = nullptr;
      32             : static void (*Py_InitializeEx)(int) = nullptr;
      33             : static void (*PyEval_InitThreads)(void) = nullptr;
      34             : static PyObject *(*Py_CompileStringExFlags)(const char *, const char *, int,
      35             :                                             void *, int) = nullptr;
      36             : 
      37             : static std::mutex gMutexGDALPython;
      38             : static bool gbHasInitializedPython = false;
      39             : static PyThreadState *gphThreadState = nullptr;
      40             : 
      41             : // Emulate Py_CompileString with Py_CompileStringExFlags
      42             : // Probably just a temporary measure for a bug of Python 3.8.0 on Windows
      43             : // https://bugs.python.org/issue37633
      44           0 : static PyObject *GDAL_Py_CompileString(const char *str, const char *filename,
      45             :                                        int start)
      46             : {
      47           0 :     return Py_CompileStringExFlags(str, filename, start, nullptr, -1);
      48             : }
      49             : 
      50             : namespace GDALPy
      51             : {
      52             : int (*Py_IsInitialized)(void) = nullptr;
      53             : PyGILState_STATE (*PyGILState_Ensure)(void) = nullptr;
      54             : void (*PyGILState_Release)(PyGILState_STATE) = nullptr;
      55             : void (*Py_SetProgramName)(const wchar_t *) = nullptr;
      56             : void (*Py_SetPythonHome)(const wchar_t *) = nullptr;
      57             : PyObject *(*PyObject_Type)(PyObject *) = nullptr;
      58             : int (*PyObject_IsInstance)(PyObject *, PyObject *) = nullptr;
      59             : PyObject *(*PyTuple_New)(size_t) = nullptr;
      60             : PyObject *(*PyBool_FromLong)(long) = nullptr;
      61             : PyObject *(*PyLong_FromLong)(long) = nullptr;
      62             : long (*PyLong_AsLong)(PyObject *) = nullptr;
      63             : PyObject *(*PyLong_FromLongLong)(GIntBig) = nullptr;
      64             : GIntBig (*PyLong_AsLongLong)(PyObject *) = nullptr;
      65             : PyObject *(*PyFloat_FromDouble)(double) = nullptr;
      66             : double (*PyFloat_AsDouble)(PyObject *) = nullptr;
      67             : PyObject *(*PyObject_Call)(PyObject *, PyObject *, PyObject *) = nullptr;
      68             : PyObject *(*PyObject_GetIter)(PyObject *) = nullptr;
      69             : PyObject *(*PyIter_Next)(PyObject *) = nullptr;
      70             : void (*Py_IncRef)(PyObject *) = nullptr;
      71             : void (*Py_DecRef)(PyObject *) = nullptr;
      72             : PyObject *(*PyErr_Occurred)(void) = nullptr;
      73             : void (*PyErr_Print)(void) = nullptr;
      74             : 
      75             : PyObject *(*Py_CompileString)(const char *, const char *, int) = nullptr;
      76             : PyObject *(*PyImport_ExecCodeModule)(const char *, PyObject *) = nullptr;
      77             : int (*PyObject_HasAttrString)(PyObject *, const char *) = nullptr;
      78             : PyObject *(*PyObject_GetAttrString)(PyObject *, const char *) = nullptr;
      79             : int (*PyObject_SetAttrString)(PyObject *, const char *, PyObject *) = nullptr;
      80             : int (*PyTuple_SetItem)(PyObject *, size_t, PyObject *) = nullptr;
      81             : void (*PyObject_Print)(PyObject *, FILE *, int) = nullptr;
      82             : Py_ssize_t (*PyBytes_Size)(PyObject *) = nullptr;
      83             : const char *(*PyBytes_AsString)(PyObject *) = nullptr;
      84             : int *(*PyBytes_AsStringAndSize)(PyObject *, char **, size_t *) = nullptr;
      85             : PyObject *(*PyBytes_FromObject)(PyObject *) = nullptr;
      86             : PyObject *(*PyBytes_FromStringAndSize)(const void *, size_t) = nullptr;
      87             : PyObject *(*PyUnicode_FromString)(const char *) = nullptr;
      88             : PyObject *(*PyUnicode_AsUTF8String)(PyObject *) = nullptr;
      89             : PyObject *(*PyImport_ImportModule)(const char *) = nullptr;
      90             : int (*PyCallable_Check)(PyObject *) = nullptr;
      91             : PyObject *(*PyDict_New)(void) = nullptr;
      92             : int (*PyDict_SetItemString)(PyObject *p, const char *key,
      93             :                             PyObject *val) = nullptr;
      94             : int (*PyDict_Next)(PyObject *p, size_t *, PyObject **, PyObject **) = nullptr;
      95             : PyObject *(*PyDict_GetItemString)(PyObject *p, const char *key) = nullptr;
      96             : PyObject *(*PyList_New)(Py_ssize_t) = nullptr;
      97             : int (*PyList_SetItem)(PyObject *, Py_ssize_t, PyObject *) = nullptr;
      98             : int (*PyArg_ParseTuple)(PyObject *, const char *, ...) = nullptr;
      99             : 
     100             : int (*PySequence_Check)(PyObject *o) = nullptr;
     101             : Py_ssize_t (*PySequence_Size)(PyObject *o) = nullptr;
     102             : PyObject *(*PySequence_GetItem)(PyObject *o, Py_ssize_t i) = nullptr;
     103             : 
     104             : void (*PyErr_Fetch)(PyObject **poPyType, PyObject **poPyValue,
     105             :                     PyObject **poPyTraceback) = nullptr;
     106             : void (*PyErr_Clear)(void) = nullptr;
     107             : const char *(*Py_GetVersion)(void) = nullptr;
     108             : 
     109             : int (*PyBuffer_FillInfo)(Py_buffer *view, PyObject *obj, void *buf, size_t len,
     110             :                          int readonly, int infoflags) = nullptr;
     111             : PyObject *(*PyMemoryView_FromBuffer)(Py_buffer *view) = nullptr;
     112             : 
     113             : PyObject *(*PyCFunction_New)(const PyMethodDef *ml, PyObject *self) = nullptr;
     114             : int (*PyModule_AddObject)(PyObject *mod, const char *name,
     115             :                           PyObject *value) = nullptr;
     116             : }  // namespace GDALPy
     117             : 
     118             : /* MinGW32 might define HAVE_DLFCN_H, so skip the unix implementation */
     119             : #if defined(HAVE_DLFCN_H) && !defined(_WIN32)
     120             : 
     121             : #include <dlfcn.h>
     122             : 
     123             : typedef void *LibraryHandle;
     124             : 
     125             : #define LOAD_NOCHECK_WITH_NAME(libHandle, x, name)                             \
     126             :     do                                                                         \
     127             :     {                                                                          \
     128             :         void *ptr = dlsym(libHandle, name);                                    \
     129             :         memcpy(&x, &ptr, sizeof(void *));                                      \
     130             :     } while (0)
     131             : 
     132             : #elif defined(_WIN32)
     133             : 
     134             : #include <windows.h>
     135             : #include <psapi.h>
     136             : 
     137             : typedef HMODULE LibraryHandle;
     138             : 
     139             : #define LOAD_NOCHECK_WITH_NAME(libHandle, x, name)                             \
     140             :     do                                                                         \
     141             :     {                                                                          \
     142             :         FARPROC ptr = GetProcAddress(libHandle, name);                         \
     143             :         memcpy(&x, &ptr, sizeof(void *));                                      \
     144             :     } while (0)
     145             : 
     146             : #endif
     147             : 
     148             : #define STRINGIFY(x) #x
     149             : 
     150             : #define LOAD_NOCHECK(libHandle, x)                                             \
     151             :     LOAD_NOCHECK_WITH_NAME(libHandle, x, STRINGIFY(x))
     152             : #define LOAD_WITH_NAME(libHandle, x, name)                                     \
     153             :     do                                                                         \
     154             :     {                                                                          \
     155             :         LOAD_NOCHECK_WITH_NAME(libHandle, x, name);                            \
     156             :         if (!x)                                                                \
     157             :         {                                                                      \
     158             :             CPLError(CE_Failure, CPLE_AppDefined, "Cannot find %s", name);     \
     159             :             return false;                                                      \
     160             :         }                                                                      \
     161             :     } while (0)
     162             : #define LOAD(libHandle, x) LOAD_WITH_NAME(libHandle, x, STRINGIFY(x))
     163             : 
     164             : /************************************************************************/
     165             : /*                          LoadPythonAPI()                             */
     166             : /************************************************************************/
     167             : 
     168             : /** Load the subset of the Python C API that we need */
     169          61 : static bool LoadPythonAPI()
     170             : {
     171             :     static int nInit = -1;
     172          61 :     if (nInit >= 0)
     173          54 :         return nInit == TRUE;
     174           7 :     nInit = FALSE;
     175             : 
     176             : #ifdef LOAD_NOCHECK_WITH_NAME
     177             :     static LibraryHandle libHandle = nullptr;
     178           7 :     const char *pszPythonSO = CPLGetConfigOption("PYTHONSO", nullptr);
     179             : #if defined(HAVE_DLFCN_H) && !defined(_WIN32)
     180             : 
     181             :     // First try in the current process in case the python symbols would
     182             :     // be already loaded
     183           7 :     libHandle = dlopen(nullptr, RTLD_LAZY);
     184          14 :     if (libHandle != nullptr &&
     185           7 :         dlsym(libHandle, "Py_SetProgramName") != nullptr)
     186             :     {
     187           2 :         CPLDebug("GDAL", "Current process has python symbols loaded");
     188             :     }
     189             :     else
     190             :     {
     191           5 :         if (libHandle)
     192           5 :             dlclose(libHandle);
     193           5 :         libHandle = nullptr;
     194             :     }
     195             : 
     196             :     // Then try the user provided shared object name
     197           7 :     if (libHandle == nullptr && pszPythonSO != nullptr)
     198             :     {
     199             :         // coverity[tainted_string]
     200           2 :         libHandle = dlopen(pszPythonSO, RTLD_NOW | RTLD_GLOBAL);
     201           2 :         if (libHandle == nullptr)
     202             :         {
     203           1 :             CPLError(CE_Failure, CPLE_AppDefined, "Cannot load %s",
     204             :                      pszPythonSO);
     205           1 :             return false;
     206             :         }
     207           1 :         if (dlsym(libHandle, "Py_SetProgramName") == nullptr)
     208             :         {
     209           1 :             CPLError(CE_Failure, CPLE_AppDefined,
     210             :                      "Cannot find Py_SetProgramName symbol in %s", pszPythonSO);
     211           1 :             return false;
     212             :         }
     213             :     }
     214             : 
     215             :     // Then try the PYTHONSO_DEFAULT if defined at compile time
     216             : #ifdef PYTHONSO_DEFAULT
     217             :     if (libHandle == nullptr)
     218             :     {
     219             :         libHandle = dlopen(PYTHONSO_DEFAULT, RTLD_NOW | RTLD_GLOBAL);
     220             :         if (!libHandle)
     221             :         {
     222             :             CPLDebug("GDAL", "%s found", PYTHONSO_DEFAULT);
     223             :         }
     224             :     }
     225             : #endif
     226             : 
     227             : #if defined(__MACH__) && defined(__APPLE__)
     228             : #define SO_EXT "dylib"
     229             : #else
     230             : #define IS_SO_EXT
     231             : #define SO_EXT "so"
     232             : #endif
     233             : 
     234           3 :     const auto tryDlopen = [](CPLString osPythonSO)
     235             :     {
     236           3 :         CPLDebug("GDAL", "Trying %s", osPythonSO.c_str());
     237           3 :         auto l_libHandle = dlopen(osPythonSO.c_str(), RTLD_NOW | RTLD_GLOBAL);
     238             : #ifdef IS_SO_EXT
     239           3 :         if (l_libHandle == nullptr)
     240             :         {
     241           0 :             osPythonSO += ".1.0";
     242           0 :             CPLDebug("GDAL", "Trying %s", osPythonSO.c_str());
     243           0 :             l_libHandle = dlopen(osPythonSO.c_str(), RTLD_NOW | RTLD_GLOBAL);
     244             :         }
     245             : #endif
     246           3 :         return l_libHandle;
     247             :     };
     248             : 
     249             :     // Then try to find the libpython that corresponds to the python binary
     250             :     // in the PATH
     251           5 :     if (libHandle == nullptr)
     252             :     {
     253           6 :         CPLString osVersion;
     254           3 :         char *pszPath = getenv("PATH");
     255           3 :         if (pszPath != nullptr
     256             : #ifdef DEBUG
     257             :             // For testing purposes
     258           3 :             && CPLTestBool(CPLGetConfigOption("GDAL_ENABLE_PYTHON_PATH", "YES"))
     259             : #endif
     260             :         )
     261             :         {
     262             :             const CPLStringList aosPathParts(
     263           6 :                 CSLTokenizeString2(pszPath, ":", 0));
     264           3 :             for (int iTry = 0; iTry < 2; ++iTry)
     265             :             {
     266          21 :                 for (const char *pszPathPart : aosPathParts)
     267             :                 {
     268             :                     struct stat sStat;
     269             :                     std::string osPythonBinary(
     270          21 :                         CPLFormFilenameSafe(pszPathPart, "python", nullptr));
     271          21 :                     if (iTry == 0)
     272          21 :                         osPythonBinary += "3";
     273          21 :                     if (lstat(osPythonBinary.c_str(), &sStat) != 0)
     274          18 :                         continue;
     275             : 
     276           3 :                     CPLDebug("GDAL", "Found %s", osPythonBinary.c_str());
     277             : 
     278           6 :                     if (S_ISLNK(sStat.st_mode)
     279             : #ifdef DEBUG
     280             :                         // For testing purposes
     281           3 :                         && CPLTestBool(CPLGetConfigOption(
     282             :                                "GDAL_ENABLE_PYTHON_SYMLINK", "YES"))
     283             : #endif
     284             :                     )
     285             :                     {
     286           6 :                         std::set<std::string> oSetAlreadyTriedLinks;
     287             :                         while (true)
     288             :                         {
     289           3 :                             oSetAlreadyTriedLinks.insert(osPythonBinary);
     290             : 
     291             :                             // If this is a symlink, hopefully the resolved
     292             :                             // name will be like "python3.6"
     293           3 :                             const int nBufSize = 2048;
     294           3 :                             std::vector<char> oFilename(nBufSize);
     295           3 :                             char *szPointerFilename = &oFilename[0];
     296             :                             int nBytes = static_cast<int>(
     297           3 :                                 readlink(osPythonBinary.c_str(),
     298           3 :                                          szPointerFilename, nBufSize));
     299           3 :                             if (nBytes != -1)
     300             :                             {
     301           3 :                                 szPointerFilename[std::min(nBytes,
     302           3 :                                                            nBufSize - 1)] = 0;
     303             :                                 CPLString osFilename(
     304           3 :                                     CPLGetFilename(szPointerFilename));
     305           3 :                                 CPLDebug("GDAL", "Which is an alias to: %s",
     306             :                                          szPointerFilename);
     307             : 
     308           3 :                                 if (STARTS_WITH(osFilename, "python"))
     309             :                                 {
     310           3 :                                     std::string osResolvedFullLink;
     311             :                                     // If the filename is again a symlink,
     312             :                                     // resolve it
     313           3 :                                     if (CPLIsFilenameRelative(osFilename))
     314             :                                     {
     315             :                                         osResolvedFullLink =
     316           9 :                                             CPLFormFilenameSafe(
     317           6 :                                                 CPLGetPathSafe(
     318             :                                                     osPythonBinary.c_str())
     319             :                                                     .c_str(),
     320           3 :                                                 osFilename, nullptr);
     321             :                                     }
     322             :                                     else
     323             :                                     {
     324           0 :                                         osResolvedFullLink = osFilename;
     325             :                                     }
     326           0 :                                     if (oSetAlreadyTriedLinks.find(
     327           3 :                                             osResolvedFullLink) ==
     328           6 :                                             oSetAlreadyTriedLinks.end() &&
     329           3 :                                         lstat(osResolvedFullLink.c_str(),
     330           6 :                                               &sStat) == 0 &&
     331           3 :                                         S_ISLNK(sStat.st_mode))
     332             :                                     {
     333             :                                         osPythonBinary =
     334           0 :                                             std::move(osResolvedFullLink);
     335           0 :                                         continue;
     336             :                                     }
     337             : 
     338             :                                     osVersion =
     339           3 :                                         osFilename.substr(strlen("python"));
     340           3 :                                     CPLDebug(
     341             :                                         "GDAL",
     342             :                                         "Python version from binary name: %s",
     343             :                                         osVersion.c_str());
     344             :                                 }
     345             :                             }
     346             :                             else
     347             :                             {
     348           0 :                                 CPLDebug("GDAL", "realink(%s) failed",
     349             :                                          osPythonBinary.c_str());
     350             :                             }
     351           3 :                             break;
     352           0 :                         }
     353             :                     }
     354             : 
     355             :                     // Otherwise, expensive way: start the binary and ask
     356             :                     // it for its version...
     357           3 :                     if (osVersion.empty())
     358             :                     {
     359           0 :                         const char *pszPrintVersion =
     360             :                             "import sys; print(str(sys.version_info[0]) +"
     361             :                             "'.' + str(sys.version_info[1]))";
     362           0 :                         const char *const apszArgv[] = {osPythonBinary.c_str(),
     363             :                                                         "-c", pszPrintVersion,
     364           0 :                                                         nullptr};
     365             :                         const CPLString osTmpFilename(
     366           0 :                             VSIMemGenerateHiddenFilename("out.txt"));
     367           0 :                         VSILFILE *fout = VSIFOpenL(osTmpFilename, "wb+");
     368           0 :                         if (CPLSpawn(apszArgv, nullptr, fout, FALSE) == 0)
     369             :                         {
     370             :                             char *pszStr =
     371           0 :                                 reinterpret_cast<char *>(VSIGetMemFileBuffer(
     372             :                                     osTmpFilename, nullptr, FALSE));
     373           0 :                             osVersion = pszStr;
     374           0 :                             if (!osVersion.empty() && osVersion.back() == '\n')
     375             :                             {
     376           0 :                                 osVersion.resize(osVersion.size() - 1);
     377             :                             }
     378           0 :                             CPLDebug("GDAL", "Python version from binary: %s",
     379             :                                      osVersion.c_str());
     380             :                         }
     381           0 :                         VSIFCloseL(fout);
     382           0 :                         VSIUnlink(osTmpFilename);
     383             :                     }
     384           3 :                     break;
     385             :                 }
     386           3 :                 if (!osVersion.empty())
     387           3 :                     break;
     388             :             }
     389             :         }
     390             : 
     391           3 :         if (!osVersion.empty())
     392             :         {
     393           3 :             libHandle = tryDlopen("libpython" + osVersion + "." SO_EXT);
     394           3 :             if (libHandle != nullptr)
     395             :             {
     396           3 :                 CPLDebug("GDAL", "... success");
     397             :             }
     398           0 :             else if (osVersion[0] == '3')
     399             :             {
     400           0 :                 libHandle = tryDlopen("libpython" + osVersion + "m." SO_EXT);
     401           0 :                 if (libHandle != nullptr)
     402             :                 {
     403           0 :                     CPLDebug("GDAL", "... success");
     404             :                 }
     405             :             }
     406             :         }
     407             :     }
     408             : 
     409             :     // Otherwise probe a few known objects.
     410             :     // Note: update doc/source/drivers/raster/vrt.rst if change
     411           5 :     if (libHandle == nullptr)
     412             :     {
     413           0 :         const char *const apszPythonSO[] = {
     414             :             "libpython3.8." SO_EXT,  "libpython3.9." SO_EXT,
     415             :             "libpython3.10." SO_EXT, "libpython3.11." SO_EXT,
     416             :             "libpython3.12." SO_EXT, "libpython3.13." SO_EXT,
     417             :             "libpython3.14." SO_EXT, "libpython3.7m." SO_EXT,
     418             :             "libpython3.6m." SO_EXT, "libpython3.5m." SO_EXT};
     419           0 :         for (size_t i = 0;
     420           0 :              libHandle == nullptr && i < CPL_ARRAYSIZE(apszPythonSO); ++i)
     421             :         {
     422           0 :             libHandle = tryDlopen(apszPythonSO[i]);
     423           0 :             if (libHandle != nullptr)
     424           0 :                 CPLDebug("GDAL", "... success");
     425             :         }
     426             :     }
     427             : 
     428             : #elif defined(_WIN32)
     429             :     CPLString osPythonBinaryUsed;
     430             : 
     431             :     // First try in the current process in case the python symbols would
     432             :     // be already loaded
     433             :     HANDLE hProcess = GetCurrentProcess();
     434             :     std::vector<HMODULE> ahModules;
     435             : 
     436             :     // 100 is not large enough when GDAL is loaded from QGIS for example
     437             :     ahModules.resize(1000);
     438             :     for (int i = 0; i < 2; i++)
     439             :     {
     440             :         DWORD nSizeNeeded = 0;
     441             :         const DWORD nSizeIn =
     442             :             static_cast<DWORD>(ahModules.size() * sizeof(HMODULE));
     443             :         EnumProcessModules(hProcess, &ahModules[0], nSizeIn, &nSizeNeeded);
     444             :         ahModules.resize(static_cast<size_t>(nSizeNeeded) / sizeof(HMODULE));
     445             :         if (nSizeNeeded <= nSizeIn)
     446             :         {
     447             :             break;
     448             :         }
     449             :     }
     450             : 
     451             :     for (size_t i = 0; i < ahModules.size(); i++)
     452             :     {
     453             :         if (GetProcAddress(ahModules[i], "Py_SetProgramName"))
     454             :         {
     455             :             libHandle = ahModules[i];
     456             :             CPLDebug("GDAL", "Current process has python symbols loaded");
     457             :             break;
     458             :         }
     459             :     }
     460             : 
     461             :     // Then try the user provided shared object name
     462             :     if (libHandle == nullptr && pszPythonSO != nullptr)
     463             :     {
     464             :         UINT uOldErrorMode;
     465             :         /* Avoid error boxes to pop up (#5211, #5525) */
     466             :         uOldErrorMode =
     467             :             SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS);
     468             : 
     469             : #if (defined(_WIN32) && _MSC_VER >= 1310) || __MSVCRT_VERSION__ >= 0x0601
     470             :         if (CPLTestBool(CPLGetConfigOption("GDAL_FILENAME_IS_UTF8", "YES")))
     471             :         {
     472             :             wchar_t *pwszFilename =
     473             :                 CPLRecodeToWChar(pszPythonSO, CPL_ENC_UTF8, CPL_ENC_UCS2);
     474             :             libHandle = LoadLibraryW(pwszFilename);
     475             :             CPLFree(pwszFilename);
     476             :         }
     477             :         else
     478             : #endif
     479             :         {
     480             :             libHandle = LoadLibrary(pszPythonSO);
     481             :         }
     482             : 
     483             :         SetErrorMode(uOldErrorMode);
     484             : 
     485             :         if (libHandle == nullptr)
     486             :         {
     487             :             CPLError(CE_Failure, CPLE_AppDefined, "Cannot load %s",
     488             :                      pszPythonSO);
     489             :             return false;
     490             :         }
     491             :         if (GetProcAddress(libHandle, "Py_SetProgramName") == nullptr)
     492             :         {
     493             :             CPLError(CE_Failure, CPLE_AppDefined,
     494             :                      "Cannot find Py_SetProgramName symbol in %s", pszPythonSO);
     495             :             return false;
     496             :         }
     497             :     }
     498             : 
     499             :     // Then try the PYTHONSO_DEFAULT if defined at compile time
     500             : #ifdef PYTHONSO_DEFAULT
     501             :     if (libHandle == nullptr)
     502             :     {
     503             :         UINT uOldErrorMode;
     504             :         uOldErrorMode =
     505             :             SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS);
     506             : 
     507             :         libHandle = LoadLibrary(PYTHONSO_DEFAULT);
     508             :         SetErrorMode(uOldErrorMode);
     509             :         if (!libHandle)
     510             :         {
     511             :             CPLDebug("GDAL", "%s found", PYTHONSO_DEFAULT);
     512             :         }
     513             :     }
     514             : #endif
     515             : 
     516             :     // Then try to find the pythonXY.dll that corresponds to the python binary
     517             :     // in the PATH
     518             :     if (libHandle == nullptr)
     519             :     {
     520             :         std::string osDLLName;
     521             :         char *pszPath = getenv("PATH");
     522             :         if (pszPath != nullptr
     523             : #ifdef DEBUG
     524             :             // For testing purposes
     525             :             && CPLTestBool(CPLGetConfigOption("GDAL_ENABLE_PYTHON_PATH", "YES"))
     526             : #endif
     527             :         )
     528             :         {
     529             :             const CPLStringList aosPathParts(
     530             :                 CSLTokenizeString2(pszPath, ";", 0));
     531             :             for (int iTry = 0; iTry < 2; ++iTry)
     532             :             {
     533             :                 for (const char *pszPathPart : aosPathParts)
     534             :                 {
     535             :                     VSIStatBufL sStat;
     536             :                     std::string osPythonBinary(CPLFormFilenameSafe(
     537             :                         pszPathPart, "python.exe", nullptr));
     538             :                     if (iTry == 1)
     539             :                         osPythonBinary += "3";
     540             :                     if (VSIStatL(osPythonBinary.c_str(), &sStat) != 0)
     541             :                         continue;
     542             : 
     543             :                     CPLDebug("GDAL", "Found %s", osPythonBinary.c_str());
     544             : 
     545             :                     {
     546             :                         // Test when dll is in the same directory as the exe
     547             :                         const CPLStringList aosFiles(VSIReadDir(pszPathPart));
     548             :                         for (const char *pszFilename : aosFiles)
     549             :                         {
     550             :                             if ((STARTS_WITH_CI(pszFilename, "python") ||
     551             :                                  // mingw64 uses libpython3.X.dll naming
     552             :                                  STARTS_WITH_CI(pszFilename, "libpython3.")) &&
     553             :                                 // do not load minimum API dll
     554             :                                 !EQUAL(pszFilename, "python3.dll") &&
     555             :                                 EQUAL(CPLGetExtensionSafe(pszFilename).c_str(),
     556             :                                       "dll"))
     557             :                             {
     558             :                                 osDLLName = CPLFormFilenameSafe(
     559             :                                     pszPathPart, pszFilename, nullptr);
     560             :                                 osPythonBinaryUsed = osPythonBinary;
     561             :                                 break;
     562             :                             }
     563             :                         }
     564             :                     }
     565             : 
     566             :                     // In python3.2, the dll is in the DLLs subdirectory
     567             :                     if (osDLLName.empty())
     568             :                     {
     569             :                         const std::string osDLLsDir(
     570             :                             CPLFormFilenameSafe(pszPathPart, "DLLs", nullptr));
     571             :                         const CPLStringList aosFiles(
     572             :                             VSIReadDir(osDLLsDir.c_str()));
     573             :                         for (const char *pszFilename : aosFiles)
     574             :                         {
     575             :                             if (STARTS_WITH_CI(pszFilename, "python") &&
     576             :                                 EQUAL(CPLGetExtensionSafe(pszFilename).c_str(),
     577             :                                       "dll"))
     578             :                             {
     579             :                                 osDLLName = CPLFormFilenameSafe(
     580             :                                     osDLLsDir.c_str(), pszFilename, nullptr);
     581             :                                 break;
     582             :                             }
     583             :                         }
     584             :                     }
     585             : 
     586             :                     break;
     587             :                 }
     588             :                 if (!osDLLName.empty())
     589             :                     break;
     590             :             }
     591             :         }
     592             : 
     593             :         if (!osDLLName.empty())
     594             :         {
     595             :             // CPLDebug("GDAL", "Trying %s", osDLLName.c_str());
     596             :             UINT uOldErrorMode;
     597             :             uOldErrorMode =
     598             :                 SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS);
     599             :             libHandle = LoadLibrary(osDLLName.c_str());
     600             :             SetErrorMode(uOldErrorMode);
     601             :             if (libHandle != nullptr)
     602             :             {
     603             :                 CPLDebug("GDAL", "%s loaded", osDLLName.c_str());
     604             :             }
     605             :         }
     606             :     }
     607             : 
     608             :     // Otherwise probe a few known objects
     609             :     // Note: update doc/source/drivers/raster/vrt.rst if change
     610             :     if (libHandle == nullptr)
     611             :     {
     612             :         const char *const apszPythonSO[] = {
     613             :             "python38.dll",  "python39.dll",  "python310.dll", "python311.dll",
     614             :             "python312.dll", "python313.dll", "python314.dll", "python37.dll",
     615             :             "python36.dll",  "python35.dll"};
     616             :         UINT uOldErrorMode;
     617             :         uOldErrorMode =
     618             :             SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS);
     619             : 
     620             :         for (size_t i = 0;
     621             :              libHandle == nullptr && i < CPL_ARRAYSIZE(apszPythonSO); ++i)
     622             :         {
     623             :             CPLDebug("GDAL", "Trying %s", apszPythonSO[i]);
     624             :             libHandle = LoadLibrary(apszPythonSO[i]);
     625             :             if (libHandle != nullptr)
     626             :                 CPLDebug("GDAL", "... success");
     627             :         }
     628             :         SetErrorMode(uOldErrorMode);
     629             :     }
     630             : #endif
     631           5 :     if (!libHandle)
     632             :     {
     633           0 :         CPLError(
     634             :             CE_Failure, CPLE_AppDefined,
     635             :             "Cannot find python/libpython. You can set the PYTHONSO "
     636             :             "configuration option to point to the a python .so/.dll/.dylib");
     637           0 :         return false;
     638             :     }
     639             : 
     640           5 :     LOAD(libHandle, Py_GetVersion);
     641          10 :     CPLString osPythonVersion(Py_GetVersion());
     642           5 :     osPythonVersion.replaceAll("\r\n", ' ');
     643           5 :     osPythonVersion.replaceAll('\n', ' ');
     644           5 :     CPLDebug("GDAL", "Python version used: %s", osPythonVersion.c_str());
     645             : 
     646           5 :     LOAD(libHandle, Py_SetProgramName);
     647           5 :     LOAD(libHandle, Py_SetPythonHome);
     648             : 
     649             : #ifdef _WIN32
     650             :     if (!osPythonBinaryUsed.empty() && getenv("PYTHONHOME") == nullptr)
     651             :     {
     652             :         std::string osPythonHome =
     653             :             CPLGetDirnameSafe(osPythonBinaryUsed.c_str());
     654             :         VSIStatBufL sStat;
     655             :         bool bOK = false;
     656             :         // Test Windows Conda layout
     657             :         std::string osDirEncodings =
     658             :             CPLFormFilenameSafe(osPythonHome.c_str(), "lib/encodings", nullptr);
     659             :         if (VSIStatL(osDirEncodings.c_str(), &sStat) == 0)
     660             :         {
     661             :             bOK = true;
     662             :         }
     663             :         else
     664             :         {
     665             :             // Test mingw64 layout
     666             :             const CPLStringList aosVersionTokens(
     667             :                 CSLTokenizeString2(osPythonVersion.c_str(), ".", 0));
     668             :             if (aosVersionTokens.size() >= 3)
     669             :             {
     670             :                 osPythonHome = CPLGetDirnameSafe(osPythonHome.c_str());
     671             :                 osDirEncodings = CPLFormFilenameSafe(
     672             :                     osPythonHome.c_str(),
     673             :                     CPLSPrintf("lib/python%s.%s/encodings", aosVersionTokens[0],
     674             :                                aosVersionTokens[1]),
     675             :                     nullptr);
     676             :                 if (VSIStatL(osDirEncodings.c_str(), &sStat) == 0)
     677             :                 {
     678             :                     bOK = true;
     679             :                 }
     680             :             }
     681             :         }
     682             :         if (bOK)
     683             :         {
     684             :             static wchar_t wszPythonHome[4096];
     685             :             wchar_t *pwszPythonHome = CPLRecodeToWChar(
     686             :                 osPythonHome.c_str(), CPL_ENC_UTF8, CPL_ENC_UCS2);
     687             :             const size_t nLength = wcslen(pwszPythonHome) + 1;
     688             :             if (nLength <= sizeof(wszPythonHome))
     689             :             {
     690             :                 CPLDebug("GDAL", "Call Py_SetPythonHome(%s)",
     691             :                          osPythonHome.c_str());
     692             :                 memcpy(wszPythonHome, pwszPythonHome,
     693             :                        nLength * sizeof(wchar_t));
     694             :                 // The string must reside in static storage
     695             :                 Py_SetPythonHome(wszPythonHome);
     696             :             }
     697             :             CPLFree(pwszPythonHome);
     698             :         }
     699             :     }
     700             : #endif
     701             : 
     702           5 :     LOAD(libHandle, PyBuffer_FillInfo);
     703           5 :     LOAD(libHandle, PyMemoryView_FromBuffer);
     704           5 :     LOAD(libHandle, PyObject_Type);
     705           5 :     LOAD(libHandle, PyObject_IsInstance);
     706           5 :     LOAD(libHandle, PyTuple_New);
     707           5 :     LOAD(libHandle, PyBool_FromLong);
     708           5 :     LOAD(libHandle, PyLong_FromLong);
     709           5 :     LOAD(libHandle, PyLong_AsLong);
     710           5 :     LOAD(libHandle, PyLong_FromLongLong);
     711           5 :     LOAD(libHandle, PyLong_AsLongLong);
     712           5 :     LOAD(libHandle, PyBytes_Size);
     713           5 :     LOAD(libHandle, PyBytes_AsString);
     714           5 :     LOAD(libHandle, PyBytes_AsStringAndSize);
     715           5 :     LOAD(libHandle, PyBytes_FromObject);
     716           5 :     LOAD(libHandle, PyBytes_FromStringAndSize);
     717             : 
     718           5 :     LOAD(libHandle, PyCFunction_New);
     719           5 :     LOAD(libHandle, PyModule_AddObject);
     720             : 
     721           5 :     LOAD_NOCHECK_WITH_NAME(libHandle, PyUnicode_FromString,
     722             :                            "PyUnicode_FromString");
     723           5 :     if (PyUnicode_FromString == nullptr)
     724             :     {
     725           0 :         LOAD_NOCHECK_WITH_NAME(libHandle, PyUnicode_FromString,
     726             :                                "PyUnicodeUCS2_FromString");
     727             :     }
     728           5 :     if (PyUnicode_FromString == nullptr)
     729             :     {
     730           0 :         LOAD_WITH_NAME(libHandle, PyUnicode_FromString,
     731             :                        "PyUnicodeUCS4_FromString");
     732             :     }
     733           5 :     LOAD_NOCHECK_WITH_NAME(libHandle, PyUnicode_AsUTF8String,
     734             :                            "PyUnicode_AsUTF8String");
     735           5 :     if (PyUnicode_AsUTF8String == nullptr)
     736             :     {
     737           0 :         LOAD_NOCHECK_WITH_NAME(libHandle, PyUnicode_AsUTF8String,
     738             :                                "PyUnicodeUCS2_AsUTF8String");
     739             :     }
     740           5 :     if (PyUnicode_AsUTF8String == nullptr)
     741             :     {
     742           0 :         LOAD_WITH_NAME(libHandle, PyUnicode_AsUTF8String,
     743             :                        "PyUnicodeUCS4_AsUTF8String");
     744             :     }
     745             : 
     746           5 :     LOAD(libHandle, PyFloat_FromDouble);
     747           5 :     LOAD(libHandle, PyFloat_AsDouble);
     748           5 :     LOAD(libHandle, PyObject_Call);
     749           5 :     LOAD(libHandle, PyObject_GetIter);
     750           5 :     LOAD(libHandle, PyIter_Next);
     751           5 :     LOAD(libHandle, Py_IncRef);
     752           5 :     LOAD(libHandle, Py_DecRef);
     753           5 :     LOAD(libHandle, PyErr_Occurred);
     754           5 :     LOAD(libHandle, PyErr_Print);
     755           5 :     LOAD(libHandle, Py_IsInitialized);
     756           5 :     LOAD(libHandle, Py_InitializeEx);
     757           5 :     LOAD(libHandle, PyEval_InitThreads);
     758           5 :     LOAD(libHandle, PyEval_SaveThread);
     759           5 :     LOAD(libHandle, PyEval_RestoreThread);
     760           5 :     LOAD(libHandle, Py_Finalize);
     761           5 :     LOAD_NOCHECK(libHandle, Py_CompileString);
     762           5 :     if (Py_CompileString == nullptr)
     763             :     {
     764             :         // Probably just a temporary measure for a bug of Python 3.8.0 on
     765             :         // Windows https://bugs.python.org/issue37633
     766           0 :         LOAD(libHandle, Py_CompileStringExFlags);
     767           0 :         Py_CompileString = GDAL_Py_CompileString;
     768             :     }
     769           5 :     LOAD(libHandle, PyImport_ExecCodeModule);
     770           5 :     LOAD(libHandle, PyObject_HasAttrString);
     771           5 :     LOAD(libHandle, PyObject_GetAttrString);
     772           5 :     LOAD(libHandle, PyObject_SetAttrString);
     773           5 :     LOAD(libHandle, PyTuple_SetItem);
     774           5 :     LOAD(libHandle, PyObject_Print);
     775           5 :     LOAD(libHandle, PyImport_ImportModule);
     776           5 :     LOAD(libHandle, PyCallable_Check);
     777           5 :     LOAD(libHandle, PyDict_New);
     778           5 :     LOAD(libHandle, PyDict_SetItemString);
     779           5 :     LOAD(libHandle, PyDict_Next);
     780           5 :     LOAD(libHandle, PyDict_GetItemString);
     781           5 :     LOAD(libHandle, PyList_New);
     782           5 :     LOAD(libHandle, PyList_SetItem);
     783           5 :     LOAD(libHandle, PySequence_Check);
     784           5 :     LOAD(libHandle, PySequence_Size);
     785           5 :     LOAD(libHandle, PySequence_GetItem);
     786           5 :     LOAD(libHandle, PyArg_ParseTuple);
     787           5 :     LOAD(libHandle, PyGILState_Ensure);
     788           5 :     LOAD(libHandle, PyGILState_Release);
     789           5 :     LOAD(libHandle, PyErr_Fetch);
     790           5 :     LOAD(libHandle, PyErr_Clear);
     791             : 
     792             : #else   // LOAD_NOCHECK_WITH_NAME
     793             :     CPLError(CE_Failure, CPLE_AppDefined,
     794             :              "This platform doesn't support dynamic loading of "
     795             :              "libraries");
     796             :     return false;
     797             : #endif  // LOAD_NOCHECK_WITH_NAME
     798             : 
     799           5 :     nInit = true;
     800           5 :     return true;
     801             : }
     802             : 
     803             : //! @cond Doxygen_Suppress
     804             : 
     805             : /************************************************************************/
     806             : /*                        GDALPythonInitialize()                        */
     807             : /************************************************************************/
     808             : 
     809             : /** Call this to initialize the Python environment.
     810             :  */
     811          61 : bool GDALPythonInitialize()
     812             : {
     813         122 :     std::lock_guard<std::mutex> guard(gMutexGDALPython);
     814             : 
     815          61 :     if (!LoadPythonAPI())
     816           2 :         return false;
     817             : 
     818          59 :     int bIsInitialized = Py_IsInitialized();
     819          59 :     if (!bIsInitialized)
     820             :     {
     821           3 :         gbHasInitializedPython = true;
     822           3 :         CPLDebug("GDAL", "Before Py_Initialize()");
     823           3 :         Py_InitializeEx(0);
     824           3 :         CPLDebug("GDAL", "Py_Initialize()");
     825           3 :         PyEval_InitThreads();
     826           3 :         gphThreadState = PyEval_SaveThread();
     827             :     }
     828             : 
     829          59 :     return true;
     830             : }
     831             : 
     832             : /************************************************************************/
     833             : /*                        GDALPythonFinalize()                          */
     834             : /************************************************************************/
     835             : 
     836             : /** To be called by GDALDestroy() */
     837         429 : void GDALPythonFinalize()
     838             : {
     839         429 :     if (gbHasInitializedPython)
     840             :     {
     841           3 :         CPLDebug("GDAL", "Py_Finalize() = %p", Py_Finalize);
     842           3 :         PyEval_RestoreThread(gphThreadState);
     843           3 :         Py_Finalize();
     844           3 :         gbHasInitializedPython = false;
     845           3 :         gphThreadState = nullptr;
     846             :     }
     847         429 : }
     848             : 
     849             : namespace GDALPy
     850             : {
     851             : 
     852             : /************************************************************************/
     853             : /*                            GIL_Holder()                              */
     854             : /************************************************************************/
     855             : 
     856         653 : GIL_Holder::GIL_Holder(bool bExclusiveLock) : m_bExclusiveLock(bExclusiveLock)
     857             : {
     858         653 :     if (bExclusiveLock)
     859             :     {
     860           0 :         gMutexGDALPython.lock();
     861             :     }
     862         653 :     m_eState = PyGILState_Ensure();
     863         657 : }
     864             : 
     865             : /************************************************************************/
     866             : /*                           ~GIL_Holder()                              */
     867             : /************************************************************************/
     868             : 
     869        1314 : GIL_Holder::~GIL_Holder()
     870             : {
     871         657 :     PyGILState_Release(m_eState);
     872         657 :     if (m_bExclusiveLock)
     873             :     {
     874           0 :         gMutexGDALPython.unlock();
     875             :     }
     876             :     else
     877             :     {
     878             :     }
     879         657 : }
     880             : 
     881             : /************************************************************************/
     882             : /*                             GetString()                              */
     883             : /************************************************************************/
     884             : 
     885        1588 : CPLString GetString(PyObject *obj, bool bEmitError)
     886             : {
     887        1588 :     PyObject *unicode = PyUnicode_AsUTF8String(obj);
     888        1588 :     if (PyErr_Occurred())
     889             :     {
     890           0 :         if (bEmitError)
     891             :         {
     892           0 :             CPLError(CE_Failure, CPLE_AppDefined, "%s",
     893           0 :                      GetPyExceptionString().c_str());
     894             :         }
     895           0 :         return CPLString();
     896             :     }
     897             : 
     898        1588 :     const char *pszRet = PyBytes_AsString(unicode);
     899        3176 :     CPLString osRet = pszRet ? pszRet : "";
     900        1588 :     Py_DecRef(unicode);
     901        1588 :     return osRet;
     902             : }
     903             : 
     904             : /************************************************************************/
     905             : /*                      GetPyExceptionString()                          */
     906             : /************************************************************************/
     907             : 
     908           8 : CPLString GetPyExceptionString()
     909             : {
     910           8 :     PyObject *poPyType = nullptr;
     911           8 :     PyObject *poPyValue = nullptr;
     912           8 :     PyObject *poPyTraceback = nullptr;
     913             : 
     914           8 :     PyErr_Fetch(&poPyType, &poPyValue, &poPyTraceback);
     915           8 :     if (poPyType)
     916           8 :         Py_IncRef(poPyType);
     917           8 :     if (poPyValue)
     918           8 :         Py_IncRef(poPyValue);
     919           8 :     if (poPyTraceback)
     920           4 :         Py_IncRef(poPyTraceback);
     921             : 
     922             :     // This is a mess. traceback.format_exception/format_exception_only
     923             :     // sometimes throw exceptions themselves !
     924             :     CPLString osPythonCode(
     925             :         "import traceback\n"
     926             :         "\n"
     927             :         "def GDALFormatException2(etype, value):\n"
     928             :         "    try:\n"
     929             :         "       return ''.join(traceback.format_exception_only(etype, value))\n"
     930             :         "    except:\n"
     931             :         "       return (str(etype) + ', ' + str(value))\n"
     932             :         "\n"
     933             :         "def GDALFormatException3(etype, value, tb):\n"
     934             :         //"    print(etype, value, tb)\n"
     935             :         "    try:\n"
     936             :         "       return ''.join(traceback.format_exception(etype, value, tb))\n"
     937             :         "    except:\n"
     938          16 :         "       return (str(etype) + ', ' + str(value))\n");
     939             : 
     940           8 :     CPLString osRet("An exception occurred in exception formatting code...");
     941             : 
     942             :     static int nCounter = 0;
     943          16 :     CPLString osModuleName(CPLSPrintf("gdal_exception_%d", nCounter));
     944             :     PyObject *poCompiledString =
     945           8 :         Py_CompileString(osPythonCode, osModuleName, Py_file_input);
     946           8 :     if (poCompiledString == nullptr || PyErr_Occurred())
     947             :     {
     948           0 :         PyErr_Print();
     949             :     }
     950             :     else
     951             :     {
     952             :         PyObject *poModule =
     953           8 :             PyImport_ExecCodeModule(osModuleName, poCompiledString);
     954           8 :         CPLAssert(poModule);
     955             : 
     956           8 :         Py_DecRef(poCompiledString);
     957             : 
     958             :         PyObject *poPyGDALFormatException2 =
     959           8 :             PyObject_GetAttrString(poModule, "GDALFormatException2");
     960           8 :         CPLAssert(poPyGDALFormatException2);
     961             : 
     962             :         PyObject *poPyGDALFormatException3 =
     963           8 :             PyObject_GetAttrString(poModule, "GDALFormatException3");
     964           8 :         CPLAssert(poPyGDALFormatException3);
     965             : 
     966           8 :         Py_DecRef(poModule);
     967             : 
     968           8 :         PyObject *pyArgs = PyTuple_New(poPyTraceback ? 3 : 2);
     969           8 :         PyTuple_SetItem(pyArgs, 0, poPyType);
     970           8 :         PyTuple_SetItem(pyArgs, 1, poPyValue);
     971           8 :         if (poPyTraceback)
     972           4 :             PyTuple_SetItem(pyArgs, 2, poPyTraceback);
     973           8 :         PyObject *poPyRet = PyObject_Call(
     974           8 :             poPyTraceback ? poPyGDALFormatException3 : poPyGDALFormatException2,
     975             :             pyArgs, nullptr);
     976           8 :         Py_DecRef(pyArgs);
     977             : 
     978           8 :         if (PyErr_Occurred())
     979             :         {
     980           0 :             osRet = "An exception occurred in exception formatting code...";
     981           0 :             PyErr_Print();
     982             :         }
     983             :         else
     984             :         {
     985           8 :             osRet = GetString(poPyRet, false);
     986           8 :             Py_DecRef(poPyRet);
     987             :         }
     988             : 
     989           8 :         Py_DecRef(poPyGDALFormatException2);
     990           8 :         Py_DecRef(poPyGDALFormatException3);
     991             :     }
     992             : 
     993           8 :     if (poPyType)
     994           8 :         Py_DecRef(poPyType);
     995           8 :     if (poPyValue)
     996           8 :         Py_DecRef(poPyValue);
     997           8 :     if (poPyTraceback)
     998           4 :         Py_DecRef(poPyTraceback);
     999             : 
    1000          16 :     return osRet;
    1001             : }
    1002             : 
    1003             : /************************************************************************/
    1004             : /*                      ErrOccurredEmitCPLError()                       */
    1005             : /************************************************************************/
    1006             : 
    1007        2352 : bool ErrOccurredEmitCPLError()
    1008             : {
    1009        2352 :     if (PyErr_Occurred())
    1010             :     {
    1011           3 :         CPLError(CE_Failure, CPLE_AppDefined, "%s",
    1012           6 :                  GetPyExceptionString().c_str());
    1013           3 :         return true;
    1014             :     }
    1015        2349 :     return false;
    1016             : }
    1017             : 
    1018             : }  // namespace GDALPy
    1019             : 
    1020             : //! @endcond

Generated by: LCOV version 1.14