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
31 changes: 15 additions & 16 deletions Doc/library/argparse.rst
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ ArgumentParser objects
prefix_chars='-', fromfile_prefix_chars=None, \
argument_default=None, conflict_handler='error', \
add_help=True, allow_abbrev=True, exit_on_error=True, \
*, suggest_on_error=False, color=True)
*, suggest_on_error=True, color=True)

Create a new :class:`ArgumentParser` object. All parameters should be passed
as keyword arguments. Each parameter has its own more detailed description
Expand Down Expand Up @@ -117,7 +117,7 @@ ArgumentParser objects
error info when an error occurs. (default: ``True``)

* suggest_on_error_ - Enables suggestions for mistyped argument choices
and subparser names (default: ``False``)
and subparser names (default: ``True``)

* color_ - Allow color output (default: ``True``)

Expand All @@ -134,6 +134,9 @@ ArgumentParser objects
.. versionchanged:: 3.14
*suggest_on_error* and *color* parameters were added.

.. versionchanged:: 3.15
*suggest_on_error* default changed to ``True``.

The following sections describe how each of these are used.


Expand Down Expand Up @@ -596,13 +599,11 @@ suggest_on_error
^^^^^^^^^^^^^^^^

By default, when a user passes an invalid argument choice or subparser name,
:class:`ArgumentParser` will exit with error info and list the permissible
argument choices (if specified) or subparser names as part of the error message.

If the user would like to enable suggestions for mistyped argument choices and
subparser names, the feature can be enabled by setting ``suggest_on_error`` to
``True``. Note that this only applies for arguments when the choices specified
are strings::
:class:`ArgumentParser` will exit with error info and provide suggestions for
mistyped arguments. The error message will list the permissible argument
choices (if specified) or subparser names, along with a "maybe you meant"
suggestion if a close match is found. Note that this only applies for arguments
when the choices specified are strings::

>>> parser = argparse.ArgumentParser(description='Process some integers.',
suggest_on_error=True)
Expand All @@ -612,16 +613,14 @@ are strings::
>>> parser.parse_args(['--action', 'sumn', 1, 2, 3])
tester.py: error: argument --action: invalid choice: 'sumn', maybe you meant 'sum'? (choose from 'sum', 'max')

If you're writing code that needs to be compatible with older Python versions
and want to opportunistically use ``suggest_on_error`` when it's available, you
can set it as an attribute after initializing the parser instead of using the
keyword argument::
You can disable suggestions by setting ``suggest_on_error`` to ``False``::

>>> parser = argparse.ArgumentParser(description='Process some integers.')
>>> parser.suggest_on_error = True
>>> parser = argparse.ArgumentParser(description='Process some integers.',
suggest_on_error=False)

.. versionadded:: 3.14

.. versionchanged:: 3.15
Changed default value of ``suggest_on_error`` from ``False`` to ``True``.

color
^^^^^
Expand Down
7 changes: 7 additions & 0 deletions Doc/whatsnew/3.15.rst
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,13 @@ New modules
Improved modules
================

argparse
--------

* Changed the *suggest_on_error* parameter of :class:`argparse.ArgumentParser` to
default to ``True``. This enables suggestions for mistyped arguments by default.
(Contributed by Jakob Schluse in :gh:`140450`.)

calendar
--------

Expand Down
6 changes: 6 additions & 0 deletions Include/internal/pycore_stackref.h
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,12 @@ PyStackRef_IsError(_PyStackRef ref)
return ref.bits == Py_TAG_INVALID;
}

static inline bool
PyStackRef_IsMalformed(_PyStackRef ref)
{
return (ref.bits & Py_TAG_BITS) == Py_TAG_INVALID;
}

static inline bool
PyStackRef_IsValid(_PyStackRef ref)
{
Expand Down
4 changes: 2 additions & 2 deletions Lib/argparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -1857,7 +1857,7 @@ class ArgumentParser(_AttributeHolder, _ActionsContainer):
- exit_on_error -- Determines whether or not ArgumentParser exits with
error info when an error occurs
- suggest_on_error - Enables suggestions for mistyped argument choices
and subparser names (default: ``False``)
and subparser names (default: ``True``)
- color - Allow color output in help messages (default: ``False``)
"""

Expand All @@ -1876,7 +1876,7 @@ def __init__(self,
allow_abbrev=True,
exit_on_error=True,
*,
suggest_on_error=False,
suggest_on_error=True,
color=True,
):
superinit = super(ArgumentParser, self).__init__
Expand Down
17 changes: 9 additions & 8 deletions Lib/test/test_argparse.py
Original file line number Diff line number Diff line change
Expand Up @@ -2287,7 +2287,7 @@ class TestArgumentAndSubparserSuggestions(TestCase):
"""Test error handling and suggestion when a user makes a typo"""

def test_wrong_argument_error_with_suggestions(self):
parser = ErrorRaisingArgumentParser(suggest_on_error=True)
parser = ErrorRaisingArgumentParser()
parser.add_argument('foo', choices=['bar', 'baz'])
with self.assertRaises(ArgumentParserError) as excinfo:
parser.parse_args(('bazz',))
Expand All @@ -2307,7 +2307,7 @@ def test_wrong_argument_error_no_suggestions(self):
)

def test_wrong_argument_subparsers_with_suggestions(self):
parser = ErrorRaisingArgumentParser(suggest_on_error=True)
parser = ErrorRaisingArgumentParser()
subparsers = parser.add_subparsers(required=True)
subparsers.add_parser('foo')
subparsers.add_parser('bar')
Expand All @@ -2331,18 +2331,19 @@ def test_wrong_argument_subparsers_no_suggestions(self):
excinfo.exception.stderr,
)

def test_wrong_argument_no_suggestion_implicit(self):
parser = ErrorRaisingArgumentParser()
def test_wrong_argument_with_suggestion_explicit(self):
parser = ErrorRaisingArgumentParser(suggest_on_error=True)
parser.add_argument('foo', choices=['bar', 'baz'])
with self.assertRaises(ArgumentParserError) as excinfo:
parser.parse_args(('bazz',))
self.assertIn(
"error: argument foo: invalid choice: 'bazz' (choose from bar, baz)",
"error: argument foo: invalid choice: 'bazz', maybe you meant"
" 'baz'? (choose from bar, baz)",
excinfo.exception.stderr,
)

def test_suggestions_choices_empty(self):
parser = ErrorRaisingArgumentParser(suggest_on_error=True)
parser = ErrorRaisingArgumentParser()
parser.add_argument('foo', choices=[])
with self.assertRaises(ArgumentParserError) as excinfo:
parser.parse_args(('bazz',))
Expand All @@ -2352,7 +2353,7 @@ def test_suggestions_choices_empty(self):
)

def test_suggestions_choices_int(self):
parser = ErrorRaisingArgumentParser(suggest_on_error=True)
parser = ErrorRaisingArgumentParser()
parser.add_argument('foo', choices=[1, 2])
with self.assertRaises(ArgumentParserError) as excinfo:
parser.parse_args(('3',))
Expand All @@ -2362,7 +2363,7 @@ def test_suggestions_choices_int(self):
)

def test_suggestions_choices_mixed_types(self):
parser = ErrorRaisingArgumentParser(suggest_on_error=True)
parser = ErrorRaisingArgumentParser()
parser.add_argument('foo', choices=[1, '2'])
with self.assertRaises(ArgumentParserError) as excinfo:
parser.parse_args(('3',))
Expand Down
5 changes: 5 additions & 0 deletions Lib/test/test_capi/test_float.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import math
import random
import platform
import sys
import unittest
import warnings
Expand Down Expand Up @@ -197,6 +198,10 @@ def test_pack_unpack_roundtrip_for_nans(self):
# PyFloat_Pack/Unpack*() API. See also gh-130317 and
# e.g. https://developercommunity.visualstudio.com/t/155064
signaling = 0
if platform.machine().startswith('parisc'):
# HP PA RISC uses 0 for quiet, see:
# https://en.wikipedia.org/wiki/NaN#Encoding
signaling = 1
quiet = int(not signaling)
if size == 8:
payload = random.randint(signaling, 0x7ffffffffffff)
Expand Down
12 changes: 10 additions & 2 deletions Lib/test/test_struct.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import math
import operator
import unittest
import platform
import struct
import sys
import weakref
Expand Down Expand Up @@ -917,10 +918,17 @@ def test_half_float(self):

# Check that packing produces a bit pattern representing a quiet NaN:
# all exponent bits and the msb of the fraction should all be 1.
if platform.machine().startswith('parisc'):
# HP PA RISC uses 0 for quiet, see:
# https://en.wikipedia.org/wiki/NaN#Encoding
expected = 0x7c
else:
expected = 0x7e

packed = struct.pack('<e', math.nan)
self.assertEqual(packed[1] & 0x7e, 0x7e)
self.assertEqual(packed[1] & 0x7e, expected)
packed = struct.pack('<e', -math.nan)
self.assertEqual(packed[1] & 0x7e, 0x7e)
self.assertEqual(packed[1] & 0x7e, expected)

# Checks for round-to-even behavior
format_bits_float__rounding_list = [
Expand Down
1 change: 1 addition & 0 deletions Misc/ACKS
Original file line number Diff line number Diff line change
Expand Up @@ -1681,6 +1681,7 @@ David Scherer
Wolfgang Scherer
Felix Scherz
Hynek Schlawack
Jakob Schluse
Bob Schmertz
Gregor Schmid
Ralf Schmitt
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Change the default of ``suggest_on_error`` to ``True`` in
``argparse.ArgumentParser``.
6 changes: 5 additions & 1 deletion Modules/_dbmmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -515,8 +515,12 @@ dbm__enter__(PyObject *self, PyObject *Py_UNUSED(dummy))
static PyObject *
dbm__exit__(PyObject *self, PyObject *Py_UNUSED(args))
{
PyObject *result;
dbmobject *dp = dbmobject_CAST(self);
return _dbm_dbm_close_impl(dp);
Py_BEGIN_CRITICAL_SECTION(self);
result = _dbm_dbm_close_impl(dp);
Py_END_CRITICAL_SECTION();
return result;
}

static PyMethodDef dbm_methods[] = {
Expand Down
6 changes: 5 additions & 1 deletion Modules/_gdbmmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -690,7 +690,11 @@ gdbm__enter__(PyObject *self, PyObject *args)
static PyObject *
gdbm__exit__(PyObject *self, PyObject *args)
{
return _gdbm_gdbm_close_impl((gdbmobject *)self);
PyObject *result;
Py_BEGIN_CRITICAL_SECTION(self);
result = _gdbm_gdbm_close_impl((gdbmobject *)self);
Py_END_CRITICAL_SECTION();
return result;
}

static PyMethodDef gdbm_methods[] = {
Expand Down
4 changes: 4 additions & 0 deletions Python/ceval.c
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,10 @@ dump_item(_PyStackRef item)
printf("<NULL>");
return;
}
if (PyStackRef_IsMalformed(item)) {
printf("<INVALID>");
return;
}
if (PyStackRef_IsTaggedInt(item)) {
printf("%" PRId64, (int64_t)PyStackRef_UntagInt(item));
return;
Expand Down
Loading