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

Generated by: LCOV version 1.14