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

Generated by: LCOV version 1.14