pythoncpython-c-api

How to set the Python executable name, now that Py_SetProgramName() is deprecated?


The Python 3.12 embedding documentation for embedding gives this example:

#define PY_SSIZE_T_CLEAN
#include <Python.h>

int
main(int argc, char *argv[])
{
    wchar_t *program = Py_DecodeLocale(argv[0], NULL);
    if (program == NULL) {
        fprintf(stderr, "Fatal error: cannot decode argv[0]\n");
        exit(1);
    }
    Py_SetProgramName(program);  /* optional but recommended */
    Py_Initialize();
    PyRun_SimpleString("from time import time,ctime\n"
                       "print('Today is', ctime(time()))\n");
    if (Py_FinalizeEx() < 0) {
        exit(120);
    }
    PyMem_RawFree(program);
    return 0;
}

Although calling Py_SetProgramName() is recommended, it throws a compile warning:

test01.c:12:5: warning: 'Py_SetProgramName' is deprecated [-Wdeprecated-declarations]
    Py_SetProgramName(program);  /* optional but recommended */
    ^
/opt/python/3.11/include/python3.11/pylifecycle.h:37:1: note: 'Py_SetProgramName' has been explicitly marked deprecated here
Py_DEPRECATED(3.11) PyAPI_FUNC(void) Py_SetProgramName(const wchar_t *);
^
/opt/python/3.11/include/python3.11/pyport.h:336:54: note: expanded from macro 'Py_DEPRECATED'
#define Py_DEPRECATED(VERSION_UNUSED) __attribute__((__deprecated__))
                                                     ^
1 warning generated.

The resulting excecutable runs and if you add import sys and print(sys.executable) to the PyRun_SimpleString() argument, the correct executable name is shown.

As this was deprecated in 3.11, and although is still recommended for 3.12, I rather get rid of the warning. How should I change the program?


Solution

  • Following the alternatives mentioned in the documentation for Py_SetProgramName and succesfully merging code from Initialization with PyConfig I got this working program:

    #define PY_SSIZE_T_CLEAN
    #include <Python.h>
    
    int
    main(int argc, char *argv[])
    {
        wchar_t *program = Py_DecodeLocale(argv[0], NULL);
        if (program == NULL) {
            fprintf(stderr, "Fatal error: cannot decode argv[0]\n");
            exit(1);
        }
        PyStatus status;
        PyConfig config;
        PyConfig_InitPythonConfig(&config);
    
        status = PyConfig_SetString(&config, &config.program_name, program);
        if (PyStatus_Exception(status)) {
            goto exception;
        }
    
        status = Py_InitializeFromConfig(&config);
        if (PyStatus_Exception(status)) {
            goto exception;
        }
    
        PyRun_SimpleString(
            "import sys\n"
            "from time import time,ctime\n"
            "print('Today is', ctime(time()))\n"
            "print('executable:', sys.executable)\n"
        ); 
        if (Py_FinalizeEx() < 0) {
            exit(120);
        }
        PyMem_RawFree(program);
        PyConfig_Clear(&config);
        return 0;
    exception:
        PyConfig_Clear(&config);
        Py_ExitStatusException(status);
    }
    

    Afterwards I found that the dev (3.13) preview Documentation contained an updated example, which did away with the program variable, and showed that the config can be cleared before running the program:

    #define PY_SSIZE_T_CLEAN
    #include <Python.h>
    
    int
    main(int argc, char *argv[])
    {
        PyStatus status;
        PyConfig config;
        PyConfig_InitPythonConfig(&config);
        status = PyConfig_SetBytesString(&config, &config.program_name, argv[0]);
        if (PyStatus_Exception(status)) {
            goto exception;
        }
        status = Py_InitializeFromConfig(&config);
        if (PyStatus_Exception(status)) {
            goto exception;
        }
        PyConfig_Clear(&config);
        PyRun_SimpleString(
            "import sys\n"
            "from time import time,ctime\n"
            "print('Today is', ctime(time()))\n"
            "print('executable:', sys.executable)\n"
        );
        if (Py_FinalizeEx() < 0) {
            exit(120);
        }
        return 0;
    
      exception:
         PyConfig_Clear(&config);
         Py_ExitStatusException(status);
    }