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

Generated by: LCOV version 1.14