Coverage Report

Created: 2025-06-13 06:29

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