Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 21 additions & 13 deletions Doc/c-api/init_config.rst
Original file line number Diff line number Diff line change
Expand Up @@ -116,15 +116,14 @@ Error Handling

Get the *config* exit code.

* Set *\*exitcode* and return ``1`` if *config* has an exit code set.
* Return ``0`` if *config* has no exit code set.
Return ``0``.

Only the ``Py_InitializeFromInitConfig()`` function can set an exit
code if the ``parse_argv`` option is non-zero.
In Python 3.15, :c:func:`Py_InitializeFromInitConfig` sets an exit code if a
command line option wants to exit Python. This is no longer the case in
Python 3.16. Instead, the option is processed in :c:func:`Py_RunMain`. This
function became useless.

An exit code can be set when parsing the command line failed (exit
code ``2``) or when a command line option asks to display the command
line help (exit code ``0``).
.. deprecated:: next


Get Options
Expand Down Expand Up @@ -247,10 +246,10 @@ Initialize Python

* Return ``0`` on success.
* Set an error in *config* and return ``-1`` on error.
* Set an exit code in *config* and return ``-1`` if Python wants to
exit.

See ``PyInitConfig_GetExitcode()`` for the exit code case.
.. versionchanged:: next
The function no longer sets an exit code if a command line option wants
to exit Python. Instead, the option is processed in :c:func:`Py_RunMain`.


.. _pyinitconfig-opts:
Expand Down Expand Up @@ -690,9 +689,6 @@ Example of customized Python always running in isolated mode::

exception:
PyConfig_Clear(&config);
if (PyStatus_IsExit(status)) {
return status.exitcode;
}
/* Display the error message and exit the process with
non-zero exit code */
Py_ExitStatusException(status);
Expand Down Expand Up @@ -758,6 +754,8 @@ PyStatus

Exit code. Argument passed to ``exit()``.

.. deprecated:: next

.. c:member:: const char *err_msg

Error message.
Expand Down Expand Up @@ -788,6 +786,11 @@ PyStatus

Exit Python with the specified exit code.

.. deprecated:: next
:c:func:`Py_InitializeFromInitConfig` no longer sets an exit code if a
command line option wants to exit Python. Instead, the option is
processed in :c:func:`Py_RunMain`.

Functions to handle a status:

.. c:function:: int PyStatus_Exception(PyStatus status)
Expand All @@ -803,6 +806,11 @@ PyStatus

Is the result an exit?

.. deprecated:: next
:c:func:`Py_InitializeFromInitConfig` no longer sets an exit code if a
command line option wants to exit Python. Instead, the option is
processed in :c:func:`Py_RunMain`.

.. c:function:: void Py_ExitStatusException(PyStatus status)

Call ``exit(exitcode)`` if *status* is an exit. Print the error
Expand Down
5 changes: 5 additions & 0 deletions Doc/c-api/interp-lifecycle.rst
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,11 @@ Initializing and finalizing the interpreter
interpreter, populating the runtime configuration structure, and querying
the returned status structure.
.. versionchanged:: next
The function no longer returns an exit code if a command line option
wants to exit Python. Instead, the option is processed in
:c:func:`Py_RunMain`.
.. c:function:: int Py_IsInitialized()
Expand Down
8 changes: 8 additions & 0 deletions Doc/whatsnew/3.16.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1128,6 +1128,14 @@ Deprecated C APIs
and :c:func:`PyAsyncGen_New` are deprecated.
They are scheduled for removal in 3.18.

* Deprecate :c:func:`PyInitConfig_GetExitCode`, :c:func:`PyStatus_Exit`,
and :c:func:`PyStatus_IsExit` functions, and :c:member:`PyStatus.exitcode`
member. :c:func:`Py_InitializeFromInitConfig` and
:c:func:`Py_InitializeFromConfig` can no longer return an exit code.
Instead, if a command line option wants to exit Python, the option is
processed in :c:func:`Py_RunMain`.
(Contributed by Victor Stinner in :gh:`158080`.)

* :c:func:`PyModule_GetFilename` is no longer deprecated, but using
:c:func:`PyModule_GetFilenameObject` instead is still recommended.
(Contributed by Victor Stinner in :gh:`154757`.)
Expand Down
15 changes: 10 additions & 5 deletions Include/cpython/initconfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,19 @@ typedef struct {
enum {
_PyStatus_TYPE_OK=0,
_PyStatus_TYPE_ERROR=1,
_PyStatus_TYPE_EXIT=2
_PyStatus_TYPE_EXIT=2 // deprecated
} _type;
const char *func;
const char *err_msg;
int exitcode;
Py_DEPRECATED(3.16) int exitcode;
} PyStatus;

PyAPI_FUNC(PyStatus) PyStatus_Ok(void);
PyAPI_FUNC(PyStatus) PyStatus_Error(const char *err_msg);
PyAPI_FUNC(PyStatus) PyStatus_NoMemory(void);
PyAPI_FUNC(PyStatus) PyStatus_Exit(int exitcode);
Py_DEPRECATED(3.16) PyAPI_FUNC(PyStatus) PyStatus_Exit(int exitcode);
PyAPI_FUNC(int) PyStatus_IsError(PyStatus err);
PyAPI_FUNC(int) PyStatus_IsExit(PyStatus err);
Py_DEPRECATED(3.16) PyAPI_FUNC(int) PyStatus_IsExit(PyStatus err);
PyAPI_FUNC(int) PyStatus_Exception(PyStatus err);

/* --- PyWideStringList ------------------------------------------------ */
Expand Down Expand Up @@ -242,6 +242,11 @@ typedef struct PyConfig {
// PYTHON_PRESITE=package.module or -X presite=package.module
wchar_t *run_presite;
#endif

// If a command line option wants to exit Python, store it in this member
// and only process the option in Py_RunMain() instead of PyConfig_Read().
// If equals to -1, there is no option.
int _deferred_cmdline_option;
} PyConfig;

PyAPI_FUNC(void) PyConfig_InitPythonConfig(PyConfig *config);
Expand Down Expand Up @@ -293,7 +298,7 @@ PyAPI_FUNC(void) PyInitConfig_Free(PyInitConfig *config);

PyAPI_FUNC(int) PyInitConfig_GetError(PyInitConfig* config,
const char **err_msg);
PyAPI_FUNC(int) PyInitConfig_GetExitCode(PyInitConfig* config,
Py_DEPRECATED(3.16) PyAPI_FUNC(int) PyInitConfig_GetExitCode(PyInitConfig* config,
int *exitcode);

PyAPI_FUNC(int) PyInitConfig_HasOption(PyInitConfig *config,
Expand Down
2 changes: 2 additions & 0 deletions Include/internal/pycore_initconfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,8 @@ extern PyObject* _PyConfig_CreateXOptionsDict(const PyConfig *config);

extern void _Py_DumpPathConfig(PyThreadState *tstate);

extern int _PyConfig_ProcessDeferredCmdlineOption(PyConfig *config);


/* --- Function used for testing ---------------------------------- */

Expand Down
1 change: 1 addition & 0 deletions Lib/test/test_embed.py
Original file line number Diff line number Diff line change
Expand Up @@ -783,6 +783,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
'use_frozen_modules': not support.Py_DEBUG,
'safe_path': False,
'_is_python_build': IGNORE_CONFIG,
'_deferred_cmdline_option': 0,
}
if Py_STATS:
CONFIG_COMPAT['_pystats'] = False
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
Deprecate :c:func:`PyInitConfig_GetExitCode`, :c:func:`PyStatus_Exit`, and
:c:func:`PyStatus_IsExit` functions, and :c:member:`PyStatus.exitcode`
member. :c:func:`Py_InitializeFromInitConfig` and
:c:func:`Py_InitializeFromConfig` can no longer return an exit code.
Instead, if a command line option wants to exit Python, the option is
processed in :c:func:`Py_RunMain`. Patch by Victor Stinner.
19 changes: 12 additions & 7 deletions Modules/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -749,13 +749,21 @@ pymain_set_path0(PyObject *main_importer_path)
static void
pymain_run_python(int *exitcode)
{
int set_running_main = 0;

PyObject *main_importer_path = NULL;
PyInterpreterState *interp = _PyInterpreterState_GET();
/* pymain_repl() and pymain_run_stdin() modify the config */
PyConfig *config = (PyConfig*)_PyInterpreterState_GetConfig(interp);

// Process command line options which want to exit Python
int cmdline_exitcode = _PyConfig_ProcessDeferredCmdlineOption(config);
if (cmdline_exitcode >= 0) {
*exitcode = cmdline_exitcode;
return;
}

int set_running_main = 0;

PyObject *main_importer_path = NULL;

/* ensure path config is written into global variables */
PyStatus status = _PyPathConfig_UpdateGlobal(config);
if (_PyStatus_EXCEPTION(status)) {
Expand Down Expand Up @@ -910,10 +918,7 @@ static int
pymain_main(_PyArgv *args)
{
PyStatus status = pymain_init(args);
if (_PyStatus_IS_EXIT(status)) {
pymain_free();
return status.exitcode;
}
assert(!_PyStatus_IS_EXIT(status));
if (_PyStatus_EXCEPTION(status)) {
pymain_exit_error(status);
}
Expand Down
3 changes: 0 additions & 3 deletions Programs/_bootstrap_python.c
Original file line number Diff line number Diff line change
Expand Up @@ -104,9 +104,6 @@ main(int argc, char **argv)

error:
PyConfig_Clear(&config);
if (PyStatus_IsExit(status)) {
return status.exitcode;
}
Py_ExitStatusException(status);
}

20 changes: 8 additions & 12 deletions Programs/_testembed.c
Original file line number Diff line number Diff line change
Expand Up @@ -1722,7 +1722,7 @@ static int test_initconfig_api(void)
goto error;
}

// Set a list of UTF-8 strings (argv)
// Set a list of UTF-8 strings (xoptions)
char* xoptions[] = {"faulthandler"};
if (PyInitConfig_SetStrList(config, "xoptions",
Py_ARRAY_LENGTH(xoptions), xoptions) < 0) {
Expand Down Expand Up @@ -1814,29 +1814,25 @@ static int test_initconfig_get_api(void)

static int test_initconfig_exit(void)
{
// -h command line option is stored as PyConfig._deferred_cmdline_option
PyInitConfig *config = PyInitConfig_Create();
if (config == NULL) {
printf("Init allocation error\n");
return 1;
}

char *argv[] = {PROGRAM_NAME_UTF8, "--help"};
char *argv[] = {PROGRAM_NAME_UTF8, "-h"};
assert(PyInitConfig_SetStrList(config, "argv",
Py_ARRAY_LENGTH(argv), argv) == 0);

assert(PyInitConfig_SetInt(config, "parse_argv", 1) == 0);

assert(Py_InitializeFromInitConfig(config) < 0);

int exitcode;
assert(PyInitConfig_GetExitCode(config, &exitcode) == 1);
assert(exitcode == 0);
assert(Py_InitializeFromInitConfig(config) == 0);
PyInitConfig_Free(config);

const char *err_msg;
assert(PyInitConfig_GetError(config, &err_msg) == 1);
assert(strcmp(err_msg, "exit code 0") == 0);
const PyConfig *rt_config = _Py_GetConfig();
assert(rt_config->_deferred_cmdline_option == 'h');

PyInitConfig_Free(config);
Py_Finalize();
return 0;
}

Expand Down
8 changes: 4 additions & 4 deletions Python/getopt.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@ static const wchar_t *opt_ptr = L"";

static const _PyOS_LongOption longopts[] = {
/* name, has_arg, val (used in switch in initconfig.c) */
{L"check-hash-based-pycs", 1, 0},
{L"help-all", 0, 1},
{L"help-env", 0, 2},
{L"help-xoptions", 0, 3},
{L"check-hash-based-pycs", 1, 1},
{L"help-all", 0, 2},
{L"help-env", 0, 3},
{L"help-xoptions", 0, 4},
{NULL, 0, -1}, /* sentinel */
};

Expand Down
Loading
Loading