Skip to content
Merged
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
35 changes: 22 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`` and leave *\*exitcode* unchanged.

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 now 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,11 @@ 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 now processed in
:c:func:`Py_RunMain`.


.. _pyinitconfig-opts:
Expand Down Expand Up @@ -690,9 +690,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 +755,8 @@ PyStatus

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

.. deprecated:: next

.. c:member:: const char *err_msg

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

Exit Python with the specified exit code.

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

Functions to handle a status:

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

Is the result an exit?

.. deprecated:: next
:c:func:`Py_InitializeFromConfig` no longer sets an exit code if a
command line option wants to exit Python. Instead, the option is
now 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
5 changes: 5 additions & 0 deletions Doc/c-api/slice.rst
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ Slice Objects
length *length*, and store the length of the slice in *slicelength*. Out
of bounds indices are clipped in a manner consistent with the handling of
normal slices.
*length* must not be negative.

Return ``0`` on success and ``-1`` on error with an exception set.

Expand Down Expand Up @@ -108,6 +109,10 @@ Slice Objects
Out of bounds indices are clipped in a manner consistent with the handling
of normal slices.

*length* must not be negative.
*step* must not be zero and must not be less than ``-PY_SSIZE_T_MAX``,
as guaranteed by :c:func:`PySlice_Unpack`.

Return the length of the slice. Always successful. Doesn't call Python
code.

Expand Down
5 changes: 1 addition & 4 deletions Doc/howto/free-threading-python.rst
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,7 @@ The free-threaded build of CPython aims to provide similar thread-safety
behavior at the Python level to the default GIL-enabled build. Built-in
types like :class:`dict`, :class:`list`, and :class:`set` use internal locks
to protect against concurrent modifications in ways that behave similarly to
the GIL. However, Python has not historically guaranteed specific behavior for
concurrent modifications to these built-in types, so this should be treated
as a description of the current implementation, not a guarantee of current or
future behavior.
the GIL. See :ref:`threadsafety` for the guarantees provided by built-in types.

.. note::

Expand Down
2 changes: 1 addition & 1 deletion Doc/library/argparse.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2099,7 +2099,7 @@ Mutual exclusion
>>> group.add_argument('--bar', action='store_false')
>>> parser.parse_args([])
usage: PROG [-h] (--foo | --bar)
PROG: error: one of the arguments --foo --bar is required
PROG: error: one of the following arguments is required: --foo, --bar

Note that currently mutually exclusive argument groups do not support the
*title* and *description* arguments of
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
now 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 0, 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
3 changes: 2 additions & 1 deletion Include/cpython/object.h
Original file line number Diff line number Diff line change
Expand Up @@ -523,7 +523,8 @@ _Py_ThreadId(void)
#elif defined(__MINGW32__) && defined(_M_IX86)
tid = __readfsdword(24);
#elif defined(__MINGW32__) && defined(_M_ARM64)
tid = __getReg(18);
// x18 is the Windows ARM64 platform register and points to the TEB.
__asm__ ("mov %0, x18" : "=r" (tid));
#elif defined(__i386__)
__asm__("{movl %%gs:0, %0|mov %0, dword ptr gs:[0]}" : "=r" (tid)); // 32-bit always uses GS
#elif defined(__MACH__) && defined(__x86_64__)
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
4 changes: 2 additions & 2 deletions Lib/argparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -2499,8 +2499,8 @@ def consume_positionals(start_index):
names = [_get_action_name(action)
for action in group._group_actions
if action.help is not SUPPRESS]
msg = _('one of the arguments %s is required')
raise ArgumentError(None, msg % ' '.join(names))
msg = _('one of the following arguments is required: %s')
raise ArgumentError(None, msg % ', '.join(names))

# return the updated namespace and the extra arguments
return namespace, extras
Expand Down
6 changes: 3 additions & 3 deletions Lib/test/test_argparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -6866,7 +6866,7 @@ def test_required_exclusive(self):
args = parser.parse_intermixed_args('1 --foo 2'.split())
self.assertEqual(NS(badger=['1', '2'], foo=True, spam=None), args)
self.assertRaisesRegex(argparse.ArgumentError,
'one of the arguments --foo --spam is required',
'one of the following arguments is required: --foo, --spam',
parser.parse_intermixed_args, '1 2'.split())
self.assertEqual(group.required, True)

Expand All @@ -6882,7 +6882,7 @@ def test_required_exclusive_with_positional(self):
args = parser.parse_intermixed_args(['a', 'b'])
self.assertEqual(NS(foo=False, spam=None, badger=['a', 'b']), args)
self.assertRaisesRegex(argparse.ArgumentError,
'one of the arguments --foo --spam badger is required',
'one of the following arguments is required: --foo, --spam, badger',
parser.parse_intermixed_args, [])
self.assertRaisesRegex(argparse.ArgumentError,
'argument badger: not allowed with argument --foo',
Expand Down Expand Up @@ -7258,7 +7258,7 @@ def test_required_mutually_exclusive_args(self):
group.add_argument('--bar')
group.add_argument('--baz')
self.assertRaisesRegex(argparse.ArgumentError,
'one of the arguments --bar --baz is required',
'one of the following arguments is required: --bar, --baz',
self.parser.parse_args, [])

def test_conflicting_mutually_exclusive_args_optional_with_metavar(self):
Expand Down
Loading
Loading