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-08-01 10:10:57 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.14." SO_EXT, "libpython3.7m." SO_EXT,
     416             :             "libpython3.6m." SO_EXT, "libpython3.5m." SO_EXT};
     417           0 :         for (size_t i = 0;
     418           0 :              libHandle == nullptr && i < CPL_ARRAYSIZE(apszPythonSO); ++i)
     419             :         {
     420           0 :             libHandle = tryDlopen(apszPythonSO[i]);
     421           0 :             if (libHandle != nullptr)
     422           0 :                 CPLDebug("GDAL", "... success");
     423             :         }
     424             :     }
     425             : 
     426             : #elif defined(_WIN32)
     427             :     CPLString osPythonBinaryUsed;
     428             : 
     429             :     // First try in the current process in case the python symbols would
     430             :     // be already loaded
     431             :     HANDLE hProcess = GetCurrentProcess();
     432             :     std::vector<HMODULE> ahModules;
     433             : 
     434             :     // 100 is not large enough when GDAL is loaded from QGIS for example
     435             :     ahModules.resize(1000);
     436             :     for (int i = 0; i < 2; i++)
     437             :     {
     438             :         DWORD nSizeNeeded = 0;
     439             :         const DWORD nSizeIn =
     440             :             static_cast<DWORD>(ahModules.size() * sizeof(HMODULE));
     441             :         EnumProcessModules(hProcess, &ahModules[0], nSizeIn, &nSizeNeeded);
     442             :         ahModules.resize(static_cast<size_t>(nSizeNeeded) / sizeof(HMODULE));
     443             :         if (nSizeNeeded <= nSizeIn)
     444             :         {
     445             :             break;
     446             :         }
     447             :     }
     448             : 
     449             :     for (size_t i = 0; i < ahModules.size(); i++)
     450             :     {
     451             :         if (GetProcAddress(ahModules[i], "Py_SetProgramName"))
     452             :         {
     453             :             libHandle = ahModules[i];
     454             :             CPLDebug("GDAL", "Current process has python symbols loaded");
     455             :             break;
     456             :         }
     457             :     }
     458             : 
     459             :     // Then try the user provided shared object name
     460             :     if (libHandle == nullptr && pszPythonSO != nullptr)
     461             :     {
     462             :         UINT uOldErrorMode;
     463             :         /* Avoid error boxes to pop up (#5211, #5525) */
     464             :         uOldErrorMode =
     465             :             SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS);
     466             : 
     467             : #if (defined(_WIN32) && _MSC_VER >= 1310) || __MSVCRT_VERSION__ >= 0x0601
     468             :         if (CPLTestBool(CPLGetConfigOption("GDAL_FILENAME_IS_UTF8", "YES")))
     469             :         {
     470             :             wchar_t *pwszFilename =
     471             :                 CPLRecodeToWChar(pszPythonSO, CPL_ENC_UTF8, CPL_ENC_UCS2);
     472             :             libHandle = LoadLibraryW(pwszFilename);
     473             :             CPLFree(pwszFilename);
     474             :         }
     475             :         else
     476             : #endif
     477             :         {
     478             :             libHandle = LoadLibrary(pszPythonSO);
     479             :         }
     480             : 
     481             :         SetErrorMode(uOldErrorMode);
     482             : 
     483             :         if (libHandle == nullptr)
     484             :         {
     485             :             CPLError(CE_Failure, CPLE_AppDefined, "Cannot load %s",
     486             :                      pszPythonSO);
     487             :             return false;
     488             :         }
     489             :         if (GetProcAddress(libHandle, "Py_SetProgramName") == nullptr)
     490             :         {
     491             :             CPLError(CE_Failure, CPLE_AppDefined,
     492             :                      "Cannot find Py_SetProgramName symbol in %s", pszPythonSO);
     493             :             return false;
     494             :         }
     495             :     }
     496             : 
     497             :     // Then try the PYTHONSO_DEFAULT if defined at compile time
     498             : #ifdef PYTHONSO_DEFAULT
     499             :     if (libHandle == nullptr)
     500             :     {
     501             :         UINT uOldErrorMode;
     502             :         uOldErrorMode =
     503             :             SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS);
     504             : 
     505             :         libHandle = LoadLibrary(PYTHONSO_DEFAULT);
     506             :         SetErrorMode(uOldErrorMode);
     507             :         if (!libHandle)
     508             :         {
     509             :             CPLDebug("GDAL", "%s found", PYTHONSO_DEFAULT);
     510             :         }
     511             :     }
     512             : #endif
     513             : 
     514             :     // Then try to find the pythonXY.dll that corresponds to the python binary
     515             :     // in the PATH
     516             :     if (libHandle == nullptr)
     517             :     {
     518             :         std::string osDLLName;
     519             :         char *pszPath = getenv("PATH");
     520             :         if (pszPath != nullptr
     521             : #ifdef DEBUG
     522             :             // For testing purposes
     523             :             && CPLTestBool(CPLGetConfigOption("GDAL_ENABLE_PYTHON_PATH", "YES"))
     524             : #endif
     525             :         )
     526             :         {
     527             :             const CPLStringList aosPathParts(
     528             :                 CSLTokenizeString2(pszPath, ";", 0));
     529             :             for (int iTry = 0; iTry < 2; ++iTry)
     530             :             {
     531             :                 for (const char *pszPathPart : aosPathParts)
     532             :                 {
     533             :                     VSIStatBufL sStat;
     534             :                     std::string osPythonBinary(CPLFormFilenameSafe(
     535             :                         pszPathPart, "python.exe", nullptr));
     536             :                     if (iTry == 1)
     537             :                         osPythonBinary += "3";
     538             :                     if (VSIStatL(osPythonBinary.c_str(), &sStat) != 0)
     539             :                         continue;
     540             : 
     541             :                     CPLDebug("GDAL", "Found %s", osPythonBinary.c_str());
     542             : 
     543             :                     {
     544             :                         // Test when dll is in the same directory as the exe
     545             :                         const CPLStringList aosFiles(VSIReadDir(pszPathPart));
     546             :                         for (const char *pszFilename : aosFiles)
     547             :                         {
     548             :                             if ((STARTS_WITH_CI(pszFilename, "python") ||
     549             :                                  // mingw64 uses libpython3.X.dll naming
     550             :                                  STARTS_WITH_CI(pszFilename, "libpython3.")) &&
     551             :                                 // do not load minimum API dll
     552             :                                 !EQUAL(pszFilename, "python3.dll") &&
     553             :                                 EQUAL(CPLGetExtensionSafe(pszFilename).c_str(),
     554             :                                       "dll"))
     555             :                             {
     556             :                                 osDLLName = CPLFormFilenameSafe(
     557             :                                     pszPathPart, pszFilename, nullptr);
     558             :                                 osPythonBinaryUsed = osPythonBinary;
     559             :                                 break;
     560             :                             }
     561             :                         }
     562             :                     }
     563             : 
     564             :                     // In python3.2, the dll is in the DLLs subdirectory
     565             :                     if (osDLLName.empty())
     566             :                     {
     567             :                         const std::string osDLLsDir(
     568             :                             CPLFormFilenameSafe(pszPathPart, "DLLs", nullptr));
     569             :                         const CPLStringList aosFiles(
     570             :                             VSIReadDir(osDLLsDir.c_str()));
     571             :                         for (const char *pszFilename : aosFiles)
     572             :                         {
     573             :                             if (STARTS_WITH_CI(pszFilename, "python") &&
     574             :                                 EQUAL(CPLGetExtensionSafe(pszFilename).c_str(),
     575             :                                       "dll"))
     576             :                             {
     577             :                                 osDLLName = CPLFormFilenameSafe(
     578             :                                     osDLLsDir.c_str(), pszFilename, nullptr);
     579             :                                 break;
     580             :                             }
     581             :                         }
     582             :                     }
     583             : 
     584             :                     break;
     585             :                 }
     586             :                 if (!osDLLName.empty())
     587             :                     break;
     588             :             }
     589             :         }
     590             : 
     591             :         if (!osDLLName.empty())
     592             :         {
     593             :             // CPLDebug("GDAL", "Trying %s", osDLLName.c_str());
     594             :             UINT uOldErrorMode;
     595             :             uOldErrorMode =
     596             :                 SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS);
     597             :             libHandle = LoadLibrary(osDLLName.c_str());
     598             :             SetErrorMode(uOldErrorMode);
     599             :             if (libHandle != nullptr)
     600             :             {
     601             :                 CPLDebug("GDAL", "%s loaded", osDLLName.c_str());
     602             :             }
     603             :         }
     604             :     }
     605             : 
     606             :     // Otherwise probe a few known objects
     607             :     // Note: update doc/source/drivers/raster/vrt.rst if change
     608             :     if (libHandle == nullptr)
     609             :     {
     610             :         const char *const apszPythonSO[] = {
     611             :             "python38.dll",  "python39.dll",  "python310.dll", "python311.dll",
     612             :             "python312.dll", "python313.dll", "python314.dll", "python37.dll",
     613             :             "python36.dll",  "python35.dll"};
     614             :         UINT uOldErrorMode;
     615             :         uOldErrorMode =
     616             :             SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS);
     617             : 
     618             :         for (size_t i = 0;
     619             :              libHandle == nullptr && i < CPL_ARRAYSIZE(apszPythonSO); ++i)
     620             :         {
     621             :             CPLDebug("GDAL", "Trying %s", apszPythonSO[i]);
     622             :             libHandle = LoadLibrary(apszPythonSO[i]);
     623             :             if (libHandle != nullptr)
     624             :                 CPLDebug("GDAL", "... success");
     625             :         }
     626             :         SetErrorMode(uOldErrorMode);
     627             :     }
     628             : #endif
     629           5 :     if (!libHandle)
     630             :     {
     631           0 :         CPLError(
     632             :             CE_Failure, CPLE_AppDefined,
     633             :             "Cannot find python/libpython. You can set the PYTHONSO "
     634             :             "configuration option to point to the a python .so/.dll/.dylib");
     635           0 :         return false;
     636             :     }
     637             : 
     638           5 :     LOAD(libHandle, Py_GetVersion);
     639          10 :     CPLString osPythonVersion(Py_GetVersion());
     640           5 :     osPythonVersion.replaceAll("\r\n", ' ');
     641           5 :     osPythonVersion.replaceAll('\n', ' ');
     642           5 :     CPLDebug("GDAL", "Python version used: %s", osPythonVersion.c_str());
     643             : 
     644           5 :     LOAD(libHandle, Py_SetProgramName);
     645           5 :     LOAD(libHandle, Py_SetPythonHome);
     646             : 
     647             : #ifdef _WIN32
     648             :     if (!osPythonBinaryUsed.empty() && getenv("PYTHONHOME") == nullptr)
     649             :     {
     650             :         std::string osPythonHome =
     651             :             CPLGetDirnameSafe(osPythonBinaryUsed.c_str());
     652             :         VSIStatBufL sStat;
     653             :         bool bOK = false;
     654             :         // Test Windows Conda layout
     655             :         std::string osDirEncodings =
     656             :             CPLFormFilenameSafe(osPythonHome.c_str(), "lib/encodings", nullptr);
     657             :         if (VSIStatL(osDirEncodings.c_str(), &sStat) == 0)
     658             :         {
     659             :             bOK = true;
     660             :         }
     661             :         else
     662             :         {
     663             :             // Test mingw64 layout
     664             :             const CPLStringList aosVersionTokens(
     665             :                 CSLTokenizeString2(osPythonVersion.c_str(), ".", 0));
     666             :             if (aosVersionTokens.size() >= 3)
     667             :             {
     668             :                 osPythonHome = CPLGetDirnameSafe(osPythonHome.c_str());
     669             :                 osDirEncodings = CPLFormFilenameSafe(
     670             :                     osPythonHome.c_str(),
     671             :                     CPLSPrintf("lib/python%s.%s/encodings", aosVersionTokens[0],
     672             :                                aosVersionTokens[1]),
     673             :                     nullptr);
     674             :                 if (VSIStatL(osDirEncodings.c_str(), &sStat) == 0)
     675             :                 {
     676             :                     bOK = true;
     677             :                 }
     678             :             }
     679             :         }
     680             :         if (bOK)
     681             :         {
     682             :             static wchar_t wszPythonHome[4096];
     683             :             wchar_t *pwszPythonHome = CPLRecodeToWChar(
     684             :                 osPythonHome.c_str(), CPL_ENC_UTF8, CPL_ENC_UCS2);
     685             :             const size_t nLength = wcslen(pwszPythonHome) + 1;
     686             :             if (nLength <= sizeof(wszPythonHome))
     687             :             {
     688             :                 CPLDebug("GDAL", "Call Py_SetPythonHome(%s)",
     689             :                          osPythonHome.c_str());
     690             :                 memcpy(wszPythonHome, pwszPythonHome,
     691             :                        nLength * sizeof(wchar_t));
     692             :                 // The string must reside in static storage
     693             :                 Py_SetPythonHome(wszPythonHome);
     694             :             }
     695             :             CPLFree(pwszPythonHome);
     696             :         }
     697             :     }
     698             : #endif
     699             : 
     700           5 :     LOAD(libHandle, PyBuffer_FillInfo);
     701           5 :     LOAD(libHandle, PyMemoryView_FromBuffer);
     702           5 :     LOAD(libHandle, PyObject_Type);
     703           5 :     LOAD(libHandle, PyObject_IsInstance);
     704           5 :     LOAD(libHandle, PyTuple_New);
     705           5 :     LOAD(libHandle, PyBool_FromLong);
     706           5 :     LOAD(libHandle, PyLong_FromLong);
     707           5 :     LOAD(libHandle, PyLong_AsLong);
     708           5 :     LOAD(libHandle, PyLong_FromLongLong);
     709           5 :     LOAD(libHandle, PyLong_AsLongLong);
     710           5 :     LOAD(libHandle, PyBytes_Size);
     711           5 :     LOAD(libHandle, PyBytes_AsString);
     712           5 :     LOAD(libHandle, PyBytes_AsStringAndSize);
     713           5 :     LOAD(libHandle, PyBytes_FromObject);
     714           5 :     LOAD(libHandle, PyBytes_FromStringAndSize);
     715             : 
     716           5 :     LOAD(libHandle, PyModule_Create2);
     717             : 
     718           5 :     LOAD_NOCHECK_WITH_NAME(libHandle, PyUnicode_FromString,
     719             :                            "PyUnicode_FromString");
     720           5 :     if (PyUnicode_FromString == nullptr)
     721             :     {
     722           0 :         LOAD_NOCHECK_WITH_NAME(libHandle, PyUnicode_FromString,
     723             :                                "PyUnicodeUCS2_FromString");
     724             :     }
     725           5 :     if (PyUnicode_FromString == nullptr)
     726             :     {
     727           0 :         LOAD_WITH_NAME(libHandle, PyUnicode_FromString,
     728             :                        "PyUnicodeUCS4_FromString");
     729             :     }
     730           5 :     LOAD_NOCHECK_WITH_NAME(libHandle, PyUnicode_AsUTF8String,
     731             :                            "PyUnicode_AsUTF8String");
     732           5 :     if (PyUnicode_AsUTF8String == nullptr)
     733             :     {
     734           0 :         LOAD_NOCHECK_WITH_NAME(libHandle, PyUnicode_AsUTF8String,
     735             :                                "PyUnicodeUCS2_AsUTF8String");
     736             :     }
     737           5 :     if (PyUnicode_AsUTF8String == nullptr)
     738             :     {
     739           0 :         LOAD_WITH_NAME(libHandle, PyUnicode_AsUTF8String,
     740             :                        "PyUnicodeUCS4_AsUTF8String");
     741             :     }
     742             : 
     743           5 :     LOAD(libHandle, PyFloat_FromDouble);
     744           5 :     LOAD(libHandle, PyFloat_AsDouble);
     745           5 :     LOAD(libHandle, PyObject_Call);
     746           5 :     LOAD(libHandle, PyObject_GetIter);
     747           5 :     LOAD(libHandle, PyIter_Next);
     748           5 :     LOAD(libHandle, Py_IncRef);
     749           5 :     LOAD(libHandle, Py_DecRef);
     750           5 :     LOAD(libHandle, PyErr_Occurred);
     751           5 :     LOAD(libHandle, PyErr_Print);
     752           5 :     LOAD(libHandle, Py_IsInitialized);
     753           5 :     LOAD(libHandle, Py_InitializeEx);
     754           5 :     LOAD(libHandle, PyEval_InitThreads);
     755           5 :     LOAD(libHandle, PyEval_SaveThread);
     756           5 :     LOAD(libHandle, PyEval_RestoreThread);
     757           5 :     LOAD(libHandle, Py_Finalize);
     758           5 :     LOAD_NOCHECK(libHandle, Py_CompileString);
     759           5 :     if (Py_CompileString == nullptr)
     760             :     {
     761             :         // Probably just a temporary measure for a bug of Python 3.8.0 on
     762             :         // Windows https://bugs.python.org/issue37633
     763           0 :         LOAD(libHandle, Py_CompileStringExFlags);
     764           0 :         Py_CompileString = GDAL_Py_CompileString;
     765             :     }
     766           5 :     LOAD(libHandle, PyImport_ExecCodeModule);
     767           5 :     LOAD(libHandle, PyObject_HasAttrString);
     768           5 :     LOAD(libHandle, PyObject_GetAttrString);
     769           5 :     LOAD(libHandle, PyObject_SetAttrString);
     770           5 :     LOAD(libHandle, PyTuple_SetItem);
     771           5 :     LOAD(libHandle, PyObject_Print);
     772           5 :     LOAD(libHandle, PyImport_ImportModule);
     773           5 :     LOAD(libHandle, PyCallable_Check);
     774           5 :     LOAD(libHandle, PyDict_New);
     775           5 :     LOAD(libHandle, PyDict_SetItemString);
     776           5 :     LOAD(libHandle, PyDict_Next);
     777           5 :     LOAD(libHandle, PyDict_GetItemString);
     778           5 :     LOAD(libHandle, PyList_New);
     779           5 :     LOAD(libHandle, PyList_SetItem);
     780           5 :     LOAD(libHandle, PySequence_Check);
     781           5 :     LOAD(libHandle, PySequence_Size);
     782           5 :     LOAD(libHandle, PySequence_GetItem);
     783           5 :     LOAD(libHandle, PyArg_ParseTuple);
     784           5 :     LOAD(libHandle, PyGILState_Ensure);
     785           5 :     LOAD(libHandle, PyGILState_Release);
     786           5 :     LOAD(libHandle, PyErr_Fetch);
     787           5 :     LOAD(libHandle, PyErr_Clear);
     788             : 
     789             : #else   // LOAD_NOCHECK_WITH_NAME
     790             :     CPLError(CE_Failure, CPLE_AppDefined,
     791             :              "This platform doesn't support dynamic loading of "
     792             :              "libraries");
     793             :     return false;
     794             : #endif  // LOAD_NOCHECK_WITH_NAME
     795             : 
     796           5 :     nInit = true;
     797           5 :     return true;
     798             : }
     799             : 
     800             : //! @cond Doxygen_Suppress
     801             : 
     802             : /************************************************************************/
     803             : /*                        GDALPythonInitialize()                        */
     804             : /************************************************************************/
     805             : 
     806             : /** Call this to initialize the Python environment.
     807             :  */
     808          58 : bool GDALPythonInitialize()
     809             : {
     810         116 :     std::lock_guard<std::mutex> guard(gMutexGDALPython);
     811             : 
     812          58 :     if (!LoadPythonAPI())
     813           2 :         return false;
     814             : 
     815          56 :     int bIsInitialized = Py_IsInitialized();
     816          56 :     if (!bIsInitialized)
     817             :     {
     818           3 :         gbHasInitializedPython = true;
     819           3 :         CPLDebug("GDAL", "Before Py_Initialize()");
     820           3 :         Py_InitializeEx(0);
     821           3 :         CPLDebug("GDAL", "Py_Initialize()");
     822           3 :         PyEval_InitThreads();
     823           3 :         gphThreadState = PyEval_SaveThread();
     824             :     }
     825             : 
     826          56 :     return true;
     827             : }
     828             : 
     829             : /************************************************************************/
     830             : /*                        GDALPythonFinalize()                          */
     831             : /************************************************************************/
     832             : 
     833             : /** To be called by GDALDestroy() */
     834         428 : void GDALPythonFinalize()
     835             : {
     836         428 :     if (gbHasInitializedPython)
     837             :     {
     838           3 :         CPLDebug("GDAL", "Py_Finalize() = %p", Py_Finalize);
     839           3 :         PyEval_RestoreThread(gphThreadState);
     840           3 :         Py_Finalize();
     841           3 :         gbHasInitializedPython = false;
     842           3 :         gphThreadState = nullptr;
     843             :     }
     844         428 : }
     845             : 
     846             : namespace GDALPy
     847             : {
     848             : 
     849             : /************************************************************************/
     850             : /*                            GIL_Holder()                              */
     851             : /************************************************************************/
     852             : 
     853         575 : GIL_Holder::GIL_Holder(bool bExclusiveLock) : m_bExclusiveLock(bExclusiveLock)
     854             : {
     855         575 :     if (bExclusiveLock)
     856             :     {
     857           0 :         gMutexGDALPython.lock();
     858             :     }
     859         575 :     m_eState = PyGILState_Ensure();
     860         575 : }
     861             : 
     862             : /************************************************************************/
     863             : /*                           ~GIL_Holder()                              */
     864             : /************************************************************************/
     865             : 
     866        1150 : GIL_Holder::~GIL_Holder()
     867             : {
     868         575 :     PyGILState_Release(m_eState);
     869         575 :     if (m_bExclusiveLock)
     870             :     {
     871           0 :         gMutexGDALPython.unlock();
     872             :     }
     873             :     else
     874             :     {
     875             :     }
     876         575 : }
     877             : 
     878             : /************************************************************************/
     879             : /*                             GetString()                              */
     880             : /************************************************************************/
     881             : 
     882        1588 : CPLString GetString(PyObject *obj, bool bEmitError)
     883             : {
     884        1588 :     PyObject *unicode = PyUnicode_AsUTF8String(obj);
     885        1588 :     if (PyErr_Occurred())
     886             :     {
     887           0 :         if (bEmitError)
     888             :         {
     889           0 :             CPLError(CE_Failure, CPLE_AppDefined, "%s",
     890           0 :                      GetPyExceptionString().c_str());
     891             :         }
     892           0 :         return CPLString();
     893             :     }
     894             : 
     895        1588 :     const char *pszRet = PyBytes_AsString(unicode);
     896        3176 :     CPLString osRet = pszRet ? pszRet : "";
     897        1588 :     Py_DecRef(unicode);
     898        1588 :     return osRet;
     899             : }
     900             : 
     901             : /************************************************************************/
     902             : /*                      GetPyExceptionString()                          */
     903             : /************************************************************************/
     904             : 
     905           8 : CPLString GetPyExceptionString()
     906             : {
     907           8 :     PyObject *poPyType = nullptr;
     908           8 :     PyObject *poPyValue = nullptr;
     909           8 :     PyObject *poPyTraceback = nullptr;
     910             : 
     911           8 :     PyErr_Fetch(&poPyType, &poPyValue, &poPyTraceback);
     912           8 :     if (poPyType)
     913           8 :         Py_IncRef(poPyType);
     914           8 :     if (poPyValue)
     915           8 :         Py_IncRef(poPyValue);
     916           8 :     if (poPyTraceback)
     917           4 :         Py_IncRef(poPyTraceback);
     918             : 
     919             :     // This is a mess. traceback.format_exception/format_exception_only
     920             :     // sometimes throw exceptions themselves !
     921             :     CPLString osPythonCode(
     922             :         "import traceback\n"
     923             :         "\n"
     924             :         "def GDALFormatException2(etype, value):\n"
     925             :         "    try:\n"
     926             :         "       return ''.join(traceback.format_exception_only(etype, value))\n"
     927             :         "    except:\n"
     928             :         "       return (str(etype) + ', ' + str(value))\n"
     929             :         "\n"
     930             :         "def GDALFormatException3(etype, value, tb):\n"
     931             :         //"    print(etype, value, tb)\n"
     932             :         "    try:\n"
     933             :         "       return ''.join(traceback.format_exception(etype, value, tb))\n"
     934             :         "    except:\n"
     935          16 :         "       return (str(etype) + ', ' + str(value))\n");
     936             : 
     937           8 :     CPLString osRet("An exception occurred in exception formatting code...");
     938             : 
     939             :     static int nCounter = 0;
     940          16 :     CPLString osModuleName(CPLSPrintf("gdal_exception_%d", nCounter));
     941             :     PyObject *poCompiledString =
     942           8 :         Py_CompileString(osPythonCode, osModuleName, Py_file_input);
     943           8 :     if (poCompiledString == nullptr || PyErr_Occurred())
     944             :     {
     945           0 :         PyErr_Print();
     946             :     }
     947             :     else
     948             :     {
     949             :         PyObject *poModule =
     950           8 :             PyImport_ExecCodeModule(osModuleName, poCompiledString);
     951           8 :         CPLAssert(poModule);
     952             : 
     953           8 :         Py_DecRef(poCompiledString);
     954             : 
     955             :         PyObject *poPyGDALFormatException2 =
     956           8 :             PyObject_GetAttrString(poModule, "GDALFormatException2");
     957           8 :         CPLAssert(poPyGDALFormatException2);
     958             : 
     959             :         PyObject *poPyGDALFormatException3 =
     960           8 :             PyObject_GetAttrString(poModule, "GDALFormatException3");
     961           8 :         CPLAssert(poPyGDALFormatException3);
     962             : 
     963           8 :         Py_DecRef(poModule);
     964             : 
     965           8 :         PyObject *pyArgs = PyTuple_New(poPyTraceback ? 3 : 2);
     966           8 :         PyTuple_SetItem(pyArgs, 0, poPyType);
     967           8 :         PyTuple_SetItem(pyArgs, 1, poPyValue);
     968           8 :         if (poPyTraceback)
     969           4 :             PyTuple_SetItem(pyArgs, 2, poPyTraceback);
     970           8 :         PyObject *poPyRet = PyObject_Call(
     971           8 :             poPyTraceback ? poPyGDALFormatException3 : poPyGDALFormatException2,
     972             :             pyArgs, nullptr);
     973           8 :         Py_DecRef(pyArgs);
     974             : 
     975           8 :         if (PyErr_Occurred())
     976             :         {
     977           0 :             osRet = "An exception occurred in exception formatting code...";
     978           0 :             PyErr_Print();
     979             :         }
     980             :         else
     981             :         {
     982           8 :             osRet = GetString(poPyRet, false);
     983           8 :             Py_DecRef(poPyRet);
     984             :         }
     985             : 
     986           8 :         Py_DecRef(poPyGDALFormatException2);
     987           8 :         Py_DecRef(poPyGDALFormatException3);
     988             :     }
     989             : 
     990           8 :     if (poPyType)
     991           8 :         Py_DecRef(poPyType);
     992           8 :     if (poPyValue)
     993           8 :         Py_DecRef(poPyValue);
     994           8 :     if (poPyTraceback)
     995           4 :         Py_DecRef(poPyTraceback);
     996             : 
     997          16 :     return osRet;
     998             : }
     999             : 
    1000             : /************************************************************************/
    1001             : /*                      ErrOccurredEmitCPLError()                       */
    1002             : /************************************************************************/
    1003             : 
    1004        2273 : bool ErrOccurredEmitCPLError()
    1005             : {
    1006        2273 :     if (PyErr_Occurred())
    1007             :     {
    1008           3 :         CPLError(CE_Failure, CPLE_AppDefined, "%s",
    1009           6 :                  GetPyExceptionString().c_str());
    1010           3 :         return true;
    1011             :     }
    1012        2270 :     return false;
    1013             : }
    1014             : 
    1015             : }  // namespace GDALPy
    1016             : 
    1017             : //! @endcond

Generated by: LCOV version 1.14