Coverage Report

Created: 2025-11-16 06:25

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