From c16e3c940eee8dc9e2c1bc5d345938ad9985a8da Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 31 May 2023 16:51:33 +0200 Subject: [PATCH] gh-105145: Remove old functions to config Python init Remove the following old functions to configure the Python initialization, deprecated in Python 3.11: * PySys_AddWarnOptionUnicode() * PySys_AddWarnOption() * PySys_AddXOption() * PySys_HasWarnOptions() * PySys_SetArgvEx() * PySys_SetArgv() * PySys_SetPath() * Py_SetPath() * Py_SetProgramName() * Py_SetPythonHome() * Py_SetStandardStreamEncoding() * _Py_SetProgramFullPath() Most of these functions are kept in the stable ABI, except: * Py_SetStandardStreamEncoding() * _Py_SetProgramFullPath() Update Doc/extending/embedding.rst and Doc/extending/extending.rst to use the new PyConfig API. _testembed.c: * check_stdio_details() now sets stdio_encoding and stdio_errors of PyConfig. * Add definitions of functions removed from the API but kept for the stable ABI. * test_init_from_config() and test_init_read_set() now use PyConfig_SetString() instead of PyConfig_SetBytesString(). Remove _Py_ClearStandardStreamEncoding() internal function. --- Doc/c-api/init.rst | 234 +----------------- Doc/c-api/init_config.rst | 38 ++- Doc/c-api/intro.rst | 6 +- Doc/c-api/sys.rst | 54 ---- Doc/extending/embedding.rst | 30 ++- Doc/extending/extending.rst | 29 ++- Doc/library/sys_path_init.rst | 3 +- Doc/using/windows.rst | 4 +- Doc/whatsnew/3.13.rst | 20 ++ Include/cpython/pylifecycle.h | 9 - Include/internal/pycore_pylifecycle.h | 2 - Include/pylifecycle.h | 6 - Include/sysmodule.h | 8 - ...-05-31-16-51-18.gh-issue-105145.b3B6lJ.rst | 17 ++ Misc/stable_abi.toml | 10 + Modules/main.c | 1 - Programs/_testembed.c | 62 +++-- Python/initconfig.c | 108 -------- Python/pathconfig.c | 31 +-- Python/pylifecycle.c | 1 - Python/sysmodule.c | 21 +- 21 files changed, 182 insertions(+), 512 deletions(-) create mode 100644 Misc/NEWS.d/next/C API/2023-05-31-16-51-18.gh-issue-105145.b3B6lJ.rst diff --git a/Doc/c-api/init.rst b/Doc/c-api/init.rst index 26762969ef8eba5..f3690ebfab28276 100644 --- a/Doc/c-api/init.rst +++ b/Doc/c-api/init.rst @@ -29,12 +29,6 @@ The following functions can be safely called before Python is initialized: * :c:func:`PyMem_SetAllocator` * :c:func:`PyMem_SetupDebugHooks` * :c:func:`PyObject_SetArenaAllocator` - * :c:func:`Py_SetPath` - * :c:func:`Py_SetProgramName` - * :c:func:`Py_SetPythonHome` - * :c:func:`Py_SetStandardStreamEncoding` - * :c:func:`PySys_AddWarnOption` - * :c:func:`PySys_AddXOption` * :c:func:`PySys_ResetWarnOptions` * Informative functions: @@ -332,7 +326,6 @@ Initializing and finalizing the interpreter .. c:function:: void Py_Initialize() .. index:: - single: Py_SetProgramName() single: PyEval_InitThreads() single: modules (in module sys) single: path (in module sys) @@ -340,8 +333,6 @@ Initializing and finalizing the interpreter pair: module; __main__ pair: module; sys triple: module; search; path - single: PySys_SetArgv() - single: PySys_SetArgvEx() single: Py_FinalizeEx() Initialize the Python interpreter. In an application embedding Python, @@ -352,7 +343,9 @@ Initializing and finalizing the interpreter the table of loaded modules (``sys.modules``), and creates the fundamental modules :mod:`builtins`, :mod:`__main__` and :mod:`sys`. It also initializes the module search path (``sys.path``). It does not set ``sys.argv``; use - :c:func:`PySys_SetArgvEx` for that. This is a no-op when called for a second time + the new :c:type:`PyConfig` API of the :ref:`Python Initialization + Configuration ` for that. This is a no-op when called for a + second time (without calling :c:func:`Py_FinalizeEx` first). There is no return value; it is a fatal error if the initialization fails. @@ -425,76 +418,9 @@ Process-wide parameters ======================= -.. c:function:: int Py_SetStandardStreamEncoding(const char *encoding, const char *errors) - - .. index:: - single: Py_Initialize() - single: main() - triple: stdin; stdout; sdterr - - This API is kept for backward compatibility: setting - :c:member:`PyConfig.stdio_encoding` and :c:member:`PyConfig.stdio_errors` - should be used instead, see :ref:`Python Initialization Configuration - `. - - This function should be called before :c:func:`Py_Initialize`, if it is - called at all. It specifies which encoding and error handling to use - with standard IO, with the same meanings as in :func:`str.encode`. - - It overrides :envvar:`PYTHONIOENCODING` values, and allows embedding code - to control IO encoding when the environment variable does not work. - - *encoding* and/or *errors* may be ``NULL`` to use - :envvar:`PYTHONIOENCODING` and/or default values (depending on other - settings). - - Note that :data:`sys.stderr` always uses the "backslashreplace" error - handler, regardless of this (or any other) setting. - - If :c:func:`Py_FinalizeEx` is called, this function will need to be called - again in order to affect subsequent calls to :c:func:`Py_Initialize`. - - Returns ``0`` if successful, a nonzero value on error (e.g. calling after the - interpreter has already been initialized). - - .. versionadded:: 3.4 - - .. deprecated:: 3.11 - - -.. c:function:: void Py_SetProgramName(const wchar_t *name) - - .. index:: - single: Py_Initialize() - single: main() - single: Py_GetPath() - - This API is kept for backward compatibility: setting - :c:member:`PyConfig.program_name` should be used instead, see :ref:`Python - Initialization Configuration `. - - This function should be called before :c:func:`Py_Initialize` is called for - the first time, if it is called at all. It tells the interpreter the value - of the ``argv[0]`` argument to the :c:func:`main` function of the program - (converted to wide characters). - This is used by :c:func:`Py_GetPath` and some other functions below to find - the Python run-time libraries relative to the interpreter executable. The - default value is ``'python'``. The argument should point to a - zero-terminated wide character string in static storage whose contents will not - change for the duration of the program's execution. No code in the Python - interpreter will change the contents of this storage. - - Use :c:func:`Py_DecodeLocale` to decode a bytes string to get a - :c:expr:`wchar_*` string. - - .. deprecated:: 3.11 - - .. c:function:: wchar* Py_GetProgramName() - .. index:: single: Py_SetProgramName() - - Return the program name set with :c:func:`Py_SetProgramName`, or the default. + Return the program name set with :c:member:`PyConfig.program_name`, or the default. The returned string points into static storage; the caller should not modify its value. @@ -509,7 +435,7 @@ Process-wide parameters Return the *prefix* for installed platform-independent files. This is derived through a number of complicated rules from the program name set with - :c:func:`Py_SetProgramName` and some environment variables; for example, if the + :c:member:`PyConfig.program_name` and some environment variables; for example, if the program name is ``'/usr/local/bin/python'``, the prefix is ``'/usr/local'``. The returned string points into static storage; the caller should not modify its value. This corresponds to the :makevar:`prefix` variable in the top-level @@ -528,7 +454,7 @@ Process-wide parameters Return the *exec-prefix* for installed platform-*dependent* files. This is derived through a number of complicated rules from the program name set with - :c:func:`Py_SetProgramName` and some environment variables; for example, if the + :c:member:`PyConfig.program_name` and some environment variables; for example, if the program name is ``'/usr/local/bin/python'``, the exec-prefix is ``'/usr/local'``. The returned string points into static storage; the caller should not modify its value. This corresponds to the :makevar:`exec_prefix` @@ -568,12 +494,11 @@ Process-wide parameters .. c:function:: wchar_t* Py_GetProgramFullPath() .. index:: - single: Py_SetProgramName() single: executable (in module sys) Return the full program name of the Python executable; this is computed as a side-effect of deriving the default module search path from the program name - (set by :c:func:`Py_SetProgramName` above). The returned string points into + (set by :c:member:`PyConfig.program_name`). The returned string points into static storage; the caller should not modify its value. The value is available to Python code as ``sys.executable``. @@ -589,10 +514,9 @@ Process-wide parameters .. index:: triple: module; search; path single: path (in module sys) - single: Py_SetPath() Return the default module search path; this is computed from the program name - (set by :c:func:`Py_SetProgramName` above) and some environment variables. + (set by :c:member:`PyConfig.program_name`) and some environment variables. The returned string consists of a series of directory names separated by a platform dependent delimiter character. The delimiter character is ``':'`` on Unix and macOS, ``';'`` on Windows. The returned string points into @@ -610,44 +534,6 @@ Process-wide parameters It now returns ``NULL`` if called before :c:func:`Py_Initialize`. -.. c:function:: void Py_SetPath(const wchar_t *) - - .. index:: - triple: module; search; path - single: path (in module sys) - single: Py_GetPath() - - This API is kept for backward compatibility: setting - :c:member:`PyConfig.module_search_paths` and - :c:member:`PyConfig.module_search_paths_set` should be used instead, see - :ref:`Python Initialization Configuration `. - - Set the default module search path. If this function is called before - :c:func:`Py_Initialize`, then :c:func:`Py_GetPath` won't attempt to compute a - default search path but uses the one provided instead. This is useful if - Python is embedded by an application that has full knowledge of the location - of all modules. The path components should be separated by the platform - dependent delimiter character, which is ``':'`` on Unix and macOS, ``';'`` - on Windows. - - This also causes :data:`sys.executable` to be set to the program - full path (see :c:func:`Py_GetProgramFullPath`) and for :data:`sys.prefix` and - :data:`sys.exec_prefix` to be empty. It is up to the caller to modify these - if required after calling :c:func:`Py_Initialize`. - - Use :c:func:`Py_DecodeLocale` to decode a bytes string to get a - :c:expr:`wchar_*` string. - - The path argument is copied internally, so the caller may free it after the - call completes. - - .. versionchanged:: 3.8 - The program full path is now used for :data:`sys.executable`, instead - of the program name. - - .. deprecated:: 3.11 - - .. c:function:: const char* Py_GetVersion() Return the version of this Python interpreter. This is a string that looks @@ -718,110 +604,10 @@ Process-wide parameters ``sys.version``. -.. c:function:: void PySys_SetArgvEx(int argc, wchar_t **argv, int updatepath) - - .. index:: - single: main() - single: Py_FatalError() - single: argv (in module sys) - - This API is kept for backward compatibility: setting - :c:member:`PyConfig.argv`, :c:member:`PyConfig.parse_argv` and - :c:member:`PyConfig.safe_path` should be used instead, see :ref:`Python - Initialization Configuration `. - - Set :data:`sys.argv` based on *argc* and *argv*. These parameters are - similar to those passed to the program's :c:func:`main` function with the - difference that the first entry should refer to the script file to be - executed rather than the executable hosting the Python interpreter. If there - isn't a script that will be run, the first entry in *argv* can be an empty - string. If this function fails to initialize :data:`sys.argv`, a fatal - condition is signalled using :c:func:`Py_FatalError`. - - If *updatepath* is zero, this is all the function does. If *updatepath* - is non-zero, the function also modifies :data:`sys.path` according to the - following algorithm: - - - If the name of an existing script is passed in ``argv[0]``, the absolute - path of the directory where the script is located is prepended to - :data:`sys.path`. - - Otherwise (that is, if *argc* is ``0`` or ``argv[0]`` doesn't point - to an existing file name), an empty string is prepended to - :data:`sys.path`, which is the same as prepending the current working - directory (``"."``). - - Use :c:func:`Py_DecodeLocale` to decode a bytes string to get a - :c:expr:`wchar_*` string. - - See also :c:member:`PyConfig.orig_argv` and :c:member:`PyConfig.argv` - members of the :ref:`Python Initialization Configuration `. - - .. note:: - It is recommended that applications embedding the Python interpreter - for purposes other than executing a single script pass ``0`` as *updatepath*, - and update :data:`sys.path` themselves if desired. - See `CVE-2008-5983 `_. - - On versions before 3.1.3, you can achieve the same effect by manually - popping the first :data:`sys.path` element after having called - :c:func:`PySys_SetArgv`, for example using:: - - PyRun_SimpleString("import sys; sys.path.pop(0)\n"); - - .. versionadded:: 3.1.3 - - .. XXX impl. doesn't seem consistent in allowing ``0``/``NULL`` for the params; - check w/ Guido. - - .. deprecated:: 3.11 - - -.. c:function:: void PySys_SetArgv(int argc, wchar_t **argv) - - This API is kept for backward compatibility: setting - :c:member:`PyConfig.argv` and :c:member:`PyConfig.parse_argv` should be used - instead, see :ref:`Python Initialization Configuration `. - - This function works like :c:func:`PySys_SetArgvEx` with *updatepath* set - to ``1`` unless the :program:`python` interpreter was started with the - :option:`-I`. - - Use :c:func:`Py_DecodeLocale` to decode a bytes string to get a - :c:expr:`wchar_*` string. - - See also :c:member:`PyConfig.orig_argv` and :c:member:`PyConfig.argv` - members of the :ref:`Python Initialization Configuration `. - - .. versionchanged:: 3.4 The *updatepath* value depends on :option:`-I`. - - .. deprecated:: 3.11 - - -.. c:function:: void Py_SetPythonHome(const wchar_t *home) - - This API is kept for backward compatibility: setting - :c:member:`PyConfig.home` should be used instead, see :ref:`Python - Initialization Configuration `. - - Set the default "home" directory, that is, the location of the standard - Python libraries. See :envvar:`PYTHONHOME` for the meaning of the - argument string. - - The argument should point to a zero-terminated character string in static - storage whose contents will not change for the duration of the program's - execution. No code in the Python interpreter will change the contents of - this storage. - - Use :c:func:`Py_DecodeLocale` to decode a bytes string to get a - :c:expr:`wchar_*` string. - - .. deprecated:: 3.11 - - .. c:function:: wchar_t* Py_GetPythonHome() - Return the default "home", that is, the value set by a previous call to - :c:func:`Py_SetPythonHome`, or the value of the :envvar:`PYTHONHOME` + Return the default "home", that is, the value set by + :c:member:`PyConfig.home`, or the value of the :envvar:`PYTHONHOME` environment variable if it is set. This function should not be called before :c:func:`Py_Initialize`, otherwise diff --git a/Doc/c-api/init_config.rst b/Doc/c-api/init_config.rst index 161def0b4baf3a2..5f70c45c54f757b 100644 --- a/Doc/c-api/init_config.rst +++ b/Doc/c-api/init_config.rst @@ -531,7 +531,17 @@ PyConfig .. c:member:: PyWideStringList argv - Command line arguments: :data:`sys.argv`. + .. index:: + single: main() + single: argv (in module sys) + + Set :data:`sys.argv` command line arguments based on + :c:member:`~PyConfig.argv`. These parameters are similar to those passed + to the program's :c:func:`main` function with the difference that the + first entry should refer to the script file to be executed rather than + the executable hosting the Python interpreter. If there isn't a script + that will be run, the first entry in :c:member:`~PyConfig.argv` can be an + empty string. Set :c:member:`~PyConfig.parse_argv` to ``1`` to parse :c:member:`~PyConfig.argv` the same way the regular Python parses Python @@ -572,6 +582,8 @@ PyConfig Part of the :ref:`Python Path Configuration ` output. + See also :c:member:`PyConfig.exec_prefix`. + .. c:member:: wchar_t* base_executable Python base executable: :data:`sys._base_executable`. @@ -584,6 +596,8 @@ PyConfig Part of the :ref:`Python Path Configuration ` output. + See also :c:member:`PyConfig.executable`. + .. c:member:: wchar_t* base_prefix :data:`sys.base_prefix`. @@ -592,6 +606,8 @@ PyConfig Part of the :ref:`Python Path Configuration ` output. + See also :c:member:`PyConfig.prefix`. + .. c:member:: int buffered_stdio If equals to ``0`` and :c:member:`~PyConfig.configure_c_stdio` is non-zero, @@ -700,6 +716,8 @@ PyConfig Part of the :ref:`Python Path Configuration ` output. + See also :c:member:`PyConfig.base_exec_prefix`. + .. c:member:: wchar_t* executable The absolute path of the executable binary for the Python interpreter: @@ -709,6 +727,8 @@ PyConfig Part of the :ref:`Python Path Configuration ` output. + See also :c:member:`PyConfig.base_executable`. + .. c:member:: int faulthandler Enable faulthandler? @@ -780,10 +800,8 @@ PyConfig .. c:member:: wchar_t* home - Python home directory. - - If :c:func:`Py_SetPythonHome` has been called, use its argument if it is - not ``NULL``. + Set the default Python "home" directory, that is, the location of the + standard Python libraries (see :envvar:`PYTHONHOME`). Set by the :envvar:`PYTHONHOME` environment variable. @@ -1029,12 +1047,13 @@ PyConfig Part of the :ref:`Python Path Configuration ` output. + See also :c:member:`PyConfig.base_prefix`. + .. c:member:: wchar_t* program_name Program name used to initialize :c:member:`~PyConfig.executable` and in early error messages during Python initialization. - * If :func:`Py_SetProgramName` has been called, use its argument. * On macOS, use :envvar:`PYTHONEXECUTABLE` environment variable if set. * If the ``WITH_NEXT_FRAMEWORK`` macro is defined, use :envvar:`__PYVENV_LAUNCHER__` environment variable if set. @@ -1144,9 +1163,6 @@ PyConfig :data:`sys.stderr` (but :data:`sys.stderr` always uses ``"backslashreplace"`` error handler). - If :c:func:`Py_SetStandardStreamEncoding` has been called, use its - *error* and *errors* arguments if they are not ``NULL``. - Use the :envvar:`PYTHONIOENCODING` environment variable if it is non-empty. @@ -1162,6 +1178,8 @@ PyConfig or if the LC_CTYPE locale is "C" or "POSIX". * ``"strict"`` otherwise. + See also :c:member:`PyConfig.legacy_windows_stdio`. + .. c:member:: int tracemalloc Enable tracemalloc? @@ -1509,7 +1527,7 @@ If a ``._pth`` file is present: * Set :c:member:`~PyConfig.safe_path` to ``1``. The ``__PYVENV_LAUNCHER__`` environment variable is used to set -:c:member:`PyConfig.base_executable` +:c:member:`PyConfig.base_executable`. Py_RunMain() diff --git a/Doc/c-api/intro.rst b/Doc/c-api/intro.rst index 8de76e55cd05869..9014f7e03b3600b 100644 --- a/Doc/c-api/intro.rst +++ b/Doc/c-api/intro.rst @@ -739,14 +739,14 @@ environment variable :envvar:`PYTHONHOME`, or insert additional directories in front of the standard path by setting :envvar:`PYTHONPATH`. .. index:: - single: Py_SetProgramName() single: Py_GetPath() single: Py_GetPrefix() single: Py_GetExecPrefix() single: Py_GetProgramFullPath() -The embedding application can steer the search by calling -``Py_SetProgramName(file)`` *before* calling :c:func:`Py_Initialize`. Note that +The embedding application can steer the search by setting +:c:member:`PyConfig.program_name` *before* calling +:c:func:`Py_InitializeFromConfig`. Note that :envvar:`PYTHONHOME` still overrides this and :envvar:`PYTHONPATH` is still inserted in front of the standard path. An application that requires total control has to provide its own implementation of :c:func:`Py_GetPath`, diff --git a/Doc/c-api/sys.rst b/Doc/c-api/sys.rst index 6fc8a3aff956862..ad6005b7c7a878b 100644 --- a/Doc/c-api/sys.rst +++ b/Doc/c-api/sys.rst @@ -237,46 +237,6 @@ accessible to C code. They all work with the current interpreter thread's Reset :data:`sys.warnoptions` to an empty list. This function may be called prior to :c:func:`Py_Initialize`. -.. c:function:: void PySys_AddWarnOption(const wchar_t *s) - - This API is kept for backward compatibility: setting - :c:member:`PyConfig.warnoptions` should be used instead, see :ref:`Python - Initialization Configuration `. - - Append *s* to :data:`sys.warnoptions`. This function must be called prior - to :c:func:`Py_Initialize` in order to affect the warnings filter list. - - .. deprecated:: 3.11 - -.. c:function:: void PySys_AddWarnOptionUnicode(PyObject *unicode) - - This API is kept for backward compatibility: setting - :c:member:`PyConfig.warnoptions` should be used instead, see :ref:`Python - Initialization Configuration `. - - Append *unicode* to :data:`sys.warnoptions`. - - Note: this function is not currently usable from outside the CPython - implementation, as it must be called prior to the implicit import of - :mod:`warnings` in :c:func:`Py_Initialize` to be effective, but can't be - called until enough of the runtime has been initialized to permit the - creation of Unicode objects. - - .. deprecated:: 3.11 - -.. c:function:: void PySys_SetPath(const wchar_t *path) - - This API is kept for backward compatibility: setting - :c:member:`PyConfig.module_search_paths` and - :c:member:`PyConfig.module_search_paths_set` should be used instead, see - :ref:`Python Initialization Configuration `. - - Set :data:`sys.path` to a list object of paths found in *path* which should - be a list of paths separated with the platform's search path delimiter - (``:`` on Unix, ``;`` on Windows). - - .. deprecated:: 3.11 - .. c:function:: void PySys_WriteStdout(const char *format, ...) Write the output string described by *format* to :data:`sys.stdout`. No @@ -313,20 +273,6 @@ accessible to C code. They all work with the current interpreter thread's .. versionadded:: 3.2 -.. c:function:: void PySys_AddXOption(const wchar_t *s) - - This API is kept for backward compatibility: setting - :c:member:`PyConfig.xoptions` should be used instead, see :ref:`Python - Initialization Configuration `. - - Parse *s* as a set of :option:`-X` options and add them to the current - options mapping as returned by :c:func:`PySys_GetXOptions`. This function - may be called prior to :c:func:`Py_Initialize`. - - .. versionadded:: 3.2 - - .. deprecated:: 3.11 - .. c:function:: PyObject *PySys_GetXOptions() Return the current dictionary of :option:`-X` options, similarly to diff --git a/Doc/extending/embedding.rst b/Doc/extending/embedding.rst index e64db3733440383..1470029b804779a 100644 --- a/Doc/extending/embedding.rst +++ b/Doc/extending/embedding.rst @@ -59,24 +59,36 @@ perform some operation on a file. :: 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); + + /* optional but recommended */ + status = PyConfig_SetBytesString(&config, &config.program_name, argv[0]); + if (PyStatus_Exception(status)) { + goto exception; } - Py_SetProgramName(program); /* optional but recommended */ - Py_Initialize(); + + status = Py_InitializeFromConfig(&config); + if (PyStatus_Exception(status)) { + goto exception; + } + PyConfig_Clear(&config); + 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; + + exception: + PyConfig_Clear(&config); + Py_ExitStatusException(status); } -The :c:func:`Py_SetProgramName` function should be called before -:c:func:`Py_Initialize` to inform the interpreter about paths to Python run-time +Setting :c:member:`PyConfig.program_name` should be called before +:c:func:`Py_InitializeFromConfig` to inform the interpreter about paths to Python run-time libraries. Next, the Python interpreter is initialized with :c:func:`Py_Initialize`, followed by the execution of a hard-coded Python script that prints the date and time. Afterwards, the :c:func:`Py_FinalizeEx` call shuts diff --git a/Doc/extending/extending.rst b/Doc/extending/extending.rst index c37b69f988a6b4f..ef93848840861c3 100644 --- a/Doc/extending/extending.rst +++ b/Doc/extending/extending.rst @@ -383,14 +383,15 @@ automatically unless there's an entry in the :c:data:`PyImport_Inittab` table. To add the module to the initialization table, use :c:func:`PyImport_AppendInittab`, optionally followed by an import of the module:: + #define PY_SSIZE_T_CLEAN + #include + 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); /* Add a built-in module, before Py_Initialize */ if (PyImport_AppendInittab("spam", PyInit_spam) == -1) { @@ -399,11 +400,18 @@ optionally followed by an import of the module:: } /* Pass argv[0] to the Python interpreter */ - Py_SetProgramName(program); + status = PyConfig_SetBytesString(&config, &config.program_name, argv[0]); + if (PyStatus_Exception(status)) { + goto exception; + } /* Initialize the Python interpreter. Required. If this step fails, it will be a fatal error. */ - Py_Initialize(); + status = Py_InitializeFromConfig(&config); + if (PyStatus_Exception(status)) { + goto exception; + } + PyConfig_Clear(&config); /* Optionally import the module; alternatively, import can be deferred until the embedded script @@ -414,10 +422,13 @@ optionally followed by an import of the module:: fprintf(stderr, "Error: could not import module 'spam'\n"); } - ... + // ... use Python C API here ... - PyMem_RawFree(program); return 0; + + exception: + PyConfig_Clear(&config); + Py_ExitStatusException(status); } .. note:: diff --git a/Doc/library/sys_path_init.rst b/Doc/library/sys_path_init.rst index 72c1387344c2ad0..a87a41cf829fa89 100644 --- a/Doc/library/sys_path_init.rst +++ b/Doc/library/sys_path_init.rst @@ -99,8 +99,7 @@ Embedded Python If Python is embedded within another application :c:func:`Py_InitializeFromConfig` and the :c:type:`PyConfig` structure can be used to initialize Python. The path specific -details are described at :ref:`init-path-config`. Alternatively the older :c:func:`Py_SetPath` -can be used to bypass the initialization of the module search path. +details are described at :ref:`init-path-config`. .. seealso:: diff --git a/Doc/using/windows.rst b/Doc/using/windows.rst index 43e3c72f3e1cde0..ac1ba111e6d9b36 100644 --- a/Doc/using/windows.rst +++ b/Doc/using/windows.rst @@ -1169,8 +1169,8 @@ following advice will prevent conflicts with other installations: listed. * If you are loading :file:`python3.dll` or :file:`python37.dll` in your own - executable, explicitly call :c:func:`Py_SetPath` or (at least) - :c:func:`Py_SetProgramName` before :c:func:`Py_Initialize`. + executable, explicitly set :c:member:`PyConfig.module_search_paths` before + :c:func:`Py_InitializeFromConfig`. * Clear and/or overwrite :envvar:`PYTHONPATH` and set :envvar:`PYTHONHOME` before launching :file:`python.exe` from your application. diff --git a/Doc/whatsnew/3.13.rst b/Doc/whatsnew/3.13.rst index 59c7f78b3802211..b2b0a9ce224bdc7 100644 --- a/Doc/whatsnew/3.13.rst +++ b/Doc/whatsnew/3.13.rst @@ -336,3 +336,23 @@ Removed * ``PyEval_CallMethod()``: use :c:func:`PyObject_CallMethod` instead. (Contributed by Victor Stinner in :gh:`105107`.) + +* Remove the following old functions to configure the Python initialization, + deprecated in Python 3.11: + + * ``PySys_AddWarnOptionUnicode()``: use :c:member:`PyConfig.warnoptions` instead. + * ``PySys_AddWarnOption()``: use :c:member:`PyConfig.warnoptions` instead. + * ``PySys_AddXOption()``: use :c:member:`PyConfig.xoptions` instead. + * ``PySys_HasWarnOptions()``: use :c:member:`PyConfig.xoptions` instead. + * ``PySys_SetArgvEx()``: set :c:member:`PyConfig.argv` instead. + * ``PySys_SetArgv()``: set :c:member:`PyConfig.argv` instead. + * ``PySys_SetPath()``: set :c:member:`PyConfig.module_search_paths` instead. + * ``Py_SetPath()``: set :c:member:`PyConfig.module_search_paths` instead. + * ``Py_SetProgramName()``: set :c:member:`PyConfig.program_name` instead. + * ``Py_SetPythonHome()``: set :c:member:`PyConfig.home` instead. + * ``Py_SetStandardStreamEncoding()``: set :c:member:`PyConfig.stdio_encoding` instead. + * ``_Py_SetProgramFullPath()``: set :c:member:`PyConfig.executable` instead. + + Use the new :c:type:`PyConfig` API of the :ref:`Python Initialization + Configuration ` instead (:pep:`587`), added to Python 3.8. + (Contributed by Victor Stinner in :gh:`105145`.) diff --git a/Include/cpython/pylifecycle.h b/Include/cpython/pylifecycle.h index 314a5cc5b942ac4..1ca9ee91a72b155 100644 --- a/Include/cpython/pylifecycle.h +++ b/Include/cpython/pylifecycle.h @@ -6,13 +6,6 @@ in all builds of Python */ PyAPI_FUNC(int) Py_FrozenMain(int argc, char **argv); -/* Only used by applications that embed the interpreter and need to - * override the standard encoding determination mechanism - */ -Py_DEPRECATED(3.11) PyAPI_FUNC(int) Py_SetStandardStreamEncoding( - const char *encoding, - const char *errors); - /* PEP 432 Multi-phase initialization API (Private while provisional!) */ PyAPI_FUNC(PyStatus) Py_PreInitialize( @@ -46,8 +39,6 @@ PyAPI_FUNC(void) _Py_RestoreSignals(void); PyAPI_FUNC(int) Py_FdIsInteractive(FILE *, const char *); PyAPI_FUNC(int) _Py_FdIsInteractive(FILE *fp, PyObject *filename); -Py_DEPRECATED(3.11) PyAPI_FUNC(void) _Py_SetProgramFullPath(const wchar_t *); - PyAPI_FUNC(const char *) _Py_gitidentifier(void); PyAPI_FUNC(const char *) _Py_gitversion(void); diff --git a/Include/internal/pycore_pylifecycle.h b/Include/internal/pycore_pylifecycle.h index 7cd998a704c88d7..b07c2dba8de847c 100644 --- a/Include/internal/pycore_pylifecycle.h +++ b/Include/internal/pycore_pylifecycle.h @@ -23,8 +23,6 @@ extern PyStatus _PyUnicode_InitEncodings(PyThreadState *tstate); extern int _PyUnicode_EnableLegacyWindowsFSEncoding(void); #endif -PyAPI_FUNC(void) _Py_ClearStandardStreamEncoding(void); - PyAPI_FUNC(int) _Py_IsLocaleCoercionTarget(const char *ctype_loc); /* Various one-time initializers */ diff --git a/Include/pylifecycle.h b/Include/pylifecycle.h index e4c3b09c963fe8a..0190b4c3f5dedac 100644 --- a/Include/pylifecycle.h +++ b/Include/pylifecycle.h @@ -34,18 +34,12 @@ PyAPI_FUNC(int) Py_Main(int argc, wchar_t **argv); PyAPI_FUNC(int) Py_BytesMain(int argc, char **argv); /* In pathconfig.c */ -Py_DEPRECATED(3.11) PyAPI_FUNC(void) Py_SetProgramName(const wchar_t *); PyAPI_FUNC(wchar_t *) Py_GetProgramName(void); - -Py_DEPRECATED(3.11) PyAPI_FUNC(void) Py_SetPythonHome(const wchar_t *); PyAPI_FUNC(wchar_t *) Py_GetPythonHome(void); - PyAPI_FUNC(wchar_t *) Py_GetProgramFullPath(void); - PyAPI_FUNC(wchar_t *) Py_GetPrefix(void); PyAPI_FUNC(wchar_t *) Py_GetExecPrefix(void); PyAPI_FUNC(wchar_t *) Py_GetPath(void); -Py_DEPRECATED(3.11) PyAPI_FUNC(void) Py_SetPath(const wchar_t *); #ifdef MS_WINDOWS int _Py_CheckPython3(void); #endif diff --git a/Include/sysmodule.h b/Include/sysmodule.h index 96f883870b34dc7..5fd21f5fa30a2b8 100644 --- a/Include/sysmodule.h +++ b/Include/sysmodule.h @@ -10,10 +10,6 @@ extern "C" { PyAPI_FUNC(PyObject *) PySys_GetObject(const char *); PyAPI_FUNC(int) PySys_SetObject(const char *, PyObject *); -Py_DEPRECATED(3.11) PyAPI_FUNC(void) PySys_SetArgv(int, wchar_t **); -Py_DEPRECATED(3.11) PyAPI_FUNC(void) PySys_SetArgvEx(int, wchar_t **, int); -Py_DEPRECATED(3.11) PyAPI_FUNC(void) PySys_SetPath(const wchar_t *); - PyAPI_FUNC(void) PySys_WriteStdout(const char *format, ...) Py_GCC_ATTRIBUTE((format(printf, 1, 2))); PyAPI_FUNC(void) PySys_WriteStderr(const char *format, ...) @@ -22,11 +18,7 @@ PyAPI_FUNC(void) PySys_FormatStdout(const char *format, ...); PyAPI_FUNC(void) PySys_FormatStderr(const char *format, ...); PyAPI_FUNC(void) PySys_ResetWarnOptions(void); -Py_DEPRECATED(3.11) PyAPI_FUNC(void) PySys_AddWarnOption(const wchar_t *); -Py_DEPRECATED(3.11) PyAPI_FUNC(void) PySys_AddWarnOptionUnicode(PyObject *); -Py_DEPRECATED(3.11) PyAPI_FUNC(int) PySys_HasWarnOptions(void); -Py_DEPRECATED(3.11) PyAPI_FUNC(void) PySys_AddXOption(const wchar_t *); PyAPI_FUNC(PyObject *) PySys_GetXOptions(void); #if !defined(Py_LIMITED_API) diff --git a/Misc/NEWS.d/next/C API/2023-05-31-16-51-18.gh-issue-105145.b3B6lJ.rst b/Misc/NEWS.d/next/C API/2023-05-31-16-51-18.gh-issue-105145.b3B6lJ.rst new file mode 100644 index 000000000000000..a3ad765b6962301 --- /dev/null +++ b/Misc/NEWS.d/next/C API/2023-05-31-16-51-18.gh-issue-105145.b3B6lJ.rst @@ -0,0 +1,17 @@ +Remove the following old functions to configure the Python initialization, +deprecated in Python 3.11: + +* ``PySys_AddWarnOptionUnicode()`` +* ``PySys_AddWarnOption()`` +* ``PySys_AddXOption()`` +* ``PySys_HasWarnOptions()`` +* ``PySys_SetArgvEx()`` +* ``PySys_SetArgv()`` +* ``PySys_SetPath()`` +* ``Py_SetPath()`` +* ``Py_SetProgramName()`` +* ``Py_SetPythonHome()`` +* ``Py_SetStandardStreamEncoding()`` +* ``_Py_SetProgramFullPath()`` + +Patch by Victor Stinner. diff --git a/Misc/stable_abi.toml b/Misc/stable_abi.toml index ff3b09ca74c39ea..7ff4da9a1f7b6a2 100644 --- a/Misc/stable_abi.toml +++ b/Misc/stable_abi.toml @@ -1311,8 +1311,10 @@ added = '3.2' [function.PySys_AddWarnOption] added = '3.2' + abi_only = true [function.PySys_AddWarnOptionUnicode] added = '3.2' + abi_only = true [function.PySys_FormatStderr] added = '3.2' [function.PySys_FormatStdout] @@ -1321,16 +1323,20 @@ added = '3.2' [function.PySys_HasWarnOptions] added = '3.2' + abi_only = true [function.PySys_ResetWarnOptions] added = '3.2' [function.PySys_SetArgv] added = '3.2' + abi_only = true [function.PySys_SetArgvEx] added = '3.2' + abi_only = true [function.PySys_SetObject] added = '3.2' [function.PySys_SetPath] added = '3.2' + abi_only = true [function.PySys_WriteStderr] added = '3.2' [function.PySys_WriteStdout] @@ -1658,8 +1664,10 @@ added = '3.2' [function.Py_SetProgramName] added = '3.2' + abi_only = true [function.Py_SetPythonHome] added = '3.2' + abi_only = true [function.Py_SetRecursionLimit] added = '3.2' [function.Py_VaBuildValue] @@ -1990,6 +1998,7 @@ added = '3.7' # (and 3.6.1 and 3.5.3) [function.PySys_AddXOption] added = '3.7' # (and 3.6.1 and 3.5.3) + abi_only = true [function.PySys_GetXOptions] added = '3.7' # (and 3.6.1 and 3.5.3) [function.PyUnicode_AsUCS4] @@ -2020,6 +2029,7 @@ added = '3.7' # (and 3.6.1 and 3.5.3) [function.Py_SetPath] added = '3.7' # (and 3.6.1 and 3.5.3) + abi_only = true [function.PyErr_SetExcFromWindowsErr] added = '3.7' # (and 3.6.1 and 3.5.3) ifdef = 'MS_WINDOWS' diff --git a/Modules/main.c b/Modules/main.c index 7edfeb3365b4c69..a5c009321d607fd 100644 --- a/Modules/main.c +++ b/Modules/main.c @@ -636,7 +636,6 @@ pymain_free(void) remain valid after Py_Finalize(), since Py_Initialize()-Py_Finalize() can be called multiple times. */ _PyPathConfig_ClearGlobal(); - _Py_ClearStandardStreamEncoding(); _Py_ClearArgcArgv(); _PyRuntime_Finalize(); } diff --git a/Programs/_testembed.c b/Programs/_testembed.c index f78ba41fe7b4ebc..351cdc302e8cb00 100644 --- a/Programs/_testembed.c +++ b/Programs/_testembed.c @@ -14,6 +14,15 @@ #include // putenv() #include +// These functions were removed from Python 3.13 API but are still exported +// for the stable ABI. We want to test them in this program. +extern void Py_SetProgramName(const wchar_t *program_name); +extern void PySys_AddWarnOption(const wchar_t *s); +extern void PySys_AddXOption(const wchar_t *s); +extern void Py_SetPath(const wchar_t *path); +extern void Py_SetPythonHome(const wchar_t *home); + + int main_argc; char **main_argv; @@ -204,23 +213,34 @@ static int test_repeated_simple_init(void) * Test forcing a particular IO encoding *****************************************************/ -static void check_stdio_details(const char *encoding, const char * errors) +static void check_stdio_details(const wchar_t *encoding, const wchar_t *errors) { /* Output info for the test case to check */ if (encoding) { - printf("Expected encoding: %s\n", encoding); + printf("Expected encoding: %ls\n", encoding); } else { printf("Expected encoding: default\n"); } if (errors) { - printf("Expected errors: %s\n", errors); + printf("Expected errors: %ls\n", errors); } else { printf("Expected errors: default\n"); } fflush(stdout); + + PyConfig config; + _PyConfig_InitCompatConfig(&config); /* Force the given IO encoding */ - Py_SetStandardStreamEncoding(encoding, errors); - _testembed_Py_InitializeFromConfig(); + if (encoding) { + config_set_string(&config, &config.stdio_encoding, encoding); + } + if (errors) { + config_set_string(&config, &config.stdio_errors, errors); + } + config_set_program_name(&config); + init_from_config_clear(&config); + + PyRun_SimpleString( "import sys;" "print('stdin: {0.encoding}:{0.errors}'.format(sys.stdin));" @@ -237,19 +257,11 @@ static int test_forced_io_encoding(void) printf("--- Use defaults ---\n"); check_stdio_details(NULL, NULL); printf("--- Set errors only ---\n"); - check_stdio_details(NULL, "ignore"); + check_stdio_details(NULL, L"ignore"); printf("--- Set encoding only ---\n"); - check_stdio_details("iso8859-1", NULL); + check_stdio_details(L"iso8859-1", NULL); printf("--- Set encoding and errors ---\n"); - check_stdio_details("iso8859-1", "replace"); - - /* Check calling after initialization fails */ - Py_Initialize(); - - if (Py_SetStandardStreamEncoding(NULL, NULL) == 0) { - printf("Unexpected success calling Py_SetStandardStreamEncoding"); - } - Py_Finalize(); + check_stdio_details(L"iso8859-1", L"replace"); return 0; } @@ -639,11 +651,7 @@ static int test_init_from_config(void) /* FIXME: test path config: module_search_path .. dll_path */ putenv("PYTHONPLATLIBDIR=env_platlibdir"); - status = PyConfig_SetBytesString(&config, &config.platlibdir, "my_platlibdir"); - if (PyStatus_Exception(status)) { - PyConfig_Clear(&config); - Py_ExitStatusException(status); - } + config_set_string(&config, &config.platlibdir, L"my_platlibdir"); putenv("PYTHONVERBOSE=0"); Py_VerboseFlag = 0; @@ -682,12 +690,6 @@ static int test_init_from_config(void) config.buffered_stdio = 0; putenv("PYTHONIOENCODING=cp424"); - Py_SetStandardStreamEncoding("ascii", "ignore"); -#ifdef MS_WINDOWS - /* Py_SetStandardStreamEncoding() sets Py_LegacyWindowsStdioFlag to 1. - Force it to 0 through the config. */ - config.legacy_windows_stdio = 0; -#endif config_set_string(&config, &config.stdio_encoding, L"iso8859-1"); config_set_string(&config, &config.stdio_errors, L"replace"); @@ -1410,11 +1412,7 @@ static int test_init_read_set(void) PyConfig config; PyConfig_InitPythonConfig(&config); - status = PyConfig_SetBytesString(&config, &config.program_name, - "./init_read_set"); - if (PyStatus_Exception(status)) { - goto fail; - } + config_set_string(&config, &config.program_name, L"./init_read_set"); status = PyConfig_Read(&config); if (PyStatus_Exception(status)) { diff --git a/Python/initconfig.c b/Python/initconfig.c index 0d42b7ea082d61d..1dcefd478eefea9 100644 --- a/Python/initconfig.c +++ b/Python/initconfig.c @@ -514,94 +514,6 @@ _PyWideStringList_AsList(const PyWideStringList *list) } -/* --- Py_SetStandardStreamEncoding() ----------------------------- */ - -/* Helper to allow an embedding application to override the normal - * mechanism that attempts to figure out an appropriate IO encoding - */ - -static char *_Py_StandardStreamEncoding = NULL; -static char *_Py_StandardStreamErrors = NULL; - -int -Py_SetStandardStreamEncoding(const char *encoding, const char *errors) -{ - if (Py_IsInitialized()) { - /* This is too late to have any effect */ - return -1; - } - - int res = 0; - - /* Py_SetStandardStreamEncoding() can be called before Py_Initialize(), - but Py_Initialize() can change the allocator. Use a known allocator - to be able to release the memory later. */ - PyMemAllocatorEx old_alloc; - _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc); - - /* Can't call PyErr_NoMemory() on errors, as Python hasn't been - * initialised yet. - * - * However, the raw memory allocators are initialised appropriately - * as C static variables, so _PyMem_RawStrdup is OK even though - * Py_Initialize hasn't been called yet. - */ - if (encoding) { - PyMem_RawFree(_Py_StandardStreamEncoding); - _Py_StandardStreamEncoding = _PyMem_RawStrdup(encoding); - if (!_Py_StandardStreamEncoding) { - res = -2; - goto done; - } - } - if (errors) { - PyMem_RawFree(_Py_StandardStreamErrors); - _Py_StandardStreamErrors = _PyMem_RawStrdup(errors); - if (!_Py_StandardStreamErrors) { - PyMem_RawFree(_Py_StandardStreamEncoding); - _Py_StandardStreamEncoding = NULL; - res = -3; - goto done; - } - } -#ifdef MS_WINDOWS - if (_Py_StandardStreamEncoding) { -_Py_COMP_DIAG_PUSH -_Py_COMP_DIAG_IGNORE_DEPR_DECLS - /* Overriding the stream encoding implies legacy streams */ - Py_LegacyWindowsStdioFlag = 1; -_Py_COMP_DIAG_POP - } -#endif - -done: - PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc); - - return res; -} - - -void -_Py_ClearStandardStreamEncoding(void) -{ - /* Use the same allocator than Py_SetStandardStreamEncoding() */ - PyMemAllocatorEx old_alloc; - _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc); - - /* We won't need them anymore. */ - if (_Py_StandardStreamEncoding) { - PyMem_RawFree(_Py_StandardStreamEncoding); - _Py_StandardStreamEncoding = NULL; - } - if (_Py_StandardStreamErrors) { - PyMem_RawFree(_Py_StandardStreamErrors); - _Py_StandardStreamErrors = NULL; - } - - PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc); -} - - /* --- Py_GetArgcArgv() ------------------------------------------- */ void @@ -1973,26 +1885,6 @@ config_init_stdio_encoding(PyConfig *config, { PyStatus status; - /* If Py_SetStandardStreamEncoding() has been called, use its - arguments if they are not NULL. */ - if (config->stdio_encoding == NULL && _Py_StandardStreamEncoding != NULL) { - status = CONFIG_SET_BYTES_STR(config, &config->stdio_encoding, - _Py_StandardStreamEncoding, - "_Py_StandardStreamEncoding"); - if (_PyStatus_EXCEPTION(status)) { - return status; - } - } - - if (config->stdio_errors == NULL && _Py_StandardStreamErrors != NULL) { - status = CONFIG_SET_BYTES_STR(config, &config->stdio_errors, - _Py_StandardStreamErrors, - "_Py_StandardStreamErrors"); - if (_PyStatus_EXCEPTION(status)) { - return status; - } - } - // Exit if encoding and errors are defined if (config->stdio_encoding != NULL && config->stdio_errors != NULL) { return _PyStatus_OK(); diff --git a/Python/pathconfig.c b/Python/pathconfig.c index be0f97c4b204a91..afffa134e893bae 100644 --- a/Python/pathconfig.c +++ b/Python/pathconfig.c @@ -211,7 +211,8 @@ path_out_of_memory(const char *func) _Py_FatalErrorFunc(func, "out of memory"); } -void +// Removed in Python 3.13 API, but kept for the stable ABI +PyAPI_FUNC(void) Py_SetPath(const wchar_t *path) { if (path == NULL) { @@ -252,7 +253,8 @@ Py_SetPath(const wchar_t *path) } -void +// Removed in Python 3.13 API, but kept for the stable ABI +PyAPI_FUNC(void) Py_SetPythonHome(const wchar_t *home) { int has_value = home && home[0]; @@ -275,7 +277,8 @@ Py_SetPythonHome(const wchar_t *home) } -void +// Removed in Python 3.13 API, but kept for the stable ABI +PyAPI_FUNC(void) Py_SetProgramName(const wchar_t *program_name) { int has_value = program_name && program_name[0]; @@ -297,28 +300,6 @@ Py_SetProgramName(const wchar_t *program_name) } } -void -_Py_SetProgramFullPath(const wchar_t *program_full_path) -{ - int has_value = program_full_path && program_full_path[0]; - - PyMemAllocatorEx old_alloc; - _PyMem_SetDefaultAllocator(PYMEM_DOMAIN_RAW, &old_alloc); - - PyMem_RawFree(_Py_path_config.program_full_path); - _Py_path_config.program_full_path = NULL; - - if (has_value) { - _Py_path_config.program_full_path = _PyMem_RawWcsdup(program_full_path); - } - - PyMem_SetAllocator(PYMEM_DOMAIN_RAW, &old_alloc); - - if (has_value && _Py_path_config.program_full_path == NULL) { - path_out_of_memory(__func__); - } -} - wchar_t * Py_GetPath(void) diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c index 06c43459624c679..c6fbf1722ed9c2b 100644 --- a/Python/pylifecycle.c +++ b/Python/pylifecycle.c @@ -2556,7 +2556,6 @@ init_sys_streams(PyThreadState *tstate) res = _PyStatus_ERR("can't initialize sys standard streams"); done: - _Py_ClearStandardStreamEncoding(); Py_XDECREF(iomod); return res; } diff --git a/Python/sysmodule.c b/Python/sysmodule.c index 32be7ec09ebfd6c..4427e73e584e301 100644 --- a/Python/sysmodule.c +++ b/Python/sysmodule.c @@ -2614,7 +2614,8 @@ _PySys_AddWarnOptionWithError(PyThreadState *tstate, PyObject *option) return 0; } -void +// Removed in Python 3.13 API, but kept for the stable ABI +PyAPI_FUNC(void) PySys_AddWarnOptionUnicode(PyObject *option) { PyThreadState *tstate = _PyThreadState_GET(); @@ -2626,7 +2627,8 @@ PySys_AddWarnOptionUnicode(PyObject *option) } } -void +// Removed in Python 3.13 API, but kept for the stable ABI +PyAPI_FUNC(void) PySys_AddWarnOption(const wchar_t *s) { PyThreadState *tstate = _PyThreadState_GET(); @@ -2645,7 +2647,8 @@ _Py_COMP_DIAG_POP Py_DECREF(unicode); } -int +// Removed in Python 3.13 API, but kept for the stable ABI +PyAPI_FUNC(int) PySys_HasWarnOptions(void) { PyThreadState *tstate = _PyThreadState_GET(); @@ -2718,7 +2721,8 @@ _PySys_AddXOptionWithError(const wchar_t *s) return -1; } -void +// Removed in Python 3.13 API, but kept for the stable ABI +PyAPI_FUNC(void) PySys_AddXOption(const wchar_t *s) { PyThreadState *tstate = _PyThreadState_GET(); @@ -3621,7 +3625,8 @@ makepathobject(const wchar_t *path, wchar_t delim) return v; } -void +// Removed in Python 3.13 API, but kept for the stable ABI +PyAPI_FUNC(void) PySys_SetPath(const wchar_t *path) { PyObject *v; @@ -3653,7 +3658,8 @@ make_sys_argv(int argc, wchar_t * const * argv) return list; } -void +// Removed in Python 3.13 API, but kept for the stable ABI +PyAPI_FUNC(void) PySys_SetArgvEx(int argc, wchar_t **argv, int updatepath) { wchar_t* empty_argv[1] = {L""}; @@ -3697,7 +3703,8 @@ PySys_SetArgvEx(int argc, wchar_t **argv, int updatepath) } } -void +// Removed in Python 3.13 API, but kept for the stable ABI +PyAPI_FUNC(void) PySys_SetArgv(int argc, wchar_t **argv) { _Py_COMP_DIAG_PUSH