8000 Update documentation · python/cpython@b1be432 · GitHub
[go: up one dir, main page]

Skip to content

Commit b1be432

Browse files
committed
Update documentation
1 parent c9d3f99 commit b1be432

File tree

14 files changed

+127
-82
lines changed

14 files changed

+127
-82
lines changed

Doc/library/argparse.rst

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -671,6 +671,9 @@ The add_argument() method
671671

672672
* choices_ - A sequence of the allowable values for the argument.
673673

674+
* convert_choices_ - Whether to convert the choices_ using the type_ callable
675+
before checking.
676+
674677
* required_ - Whether or not the command-line option may be omitted
675678
(optionals only).
676679

@@ -1156,10 +1159,6 @@ if the argument was not one of the acceptable values::
11561159
game.py: error: argument move: invalid choice: 'fire' (choose from 'rock',
11571160
'paper', 'scissors')
11581161

1159-
Note that inclusion in the *choices* sequence is checked after any type_
1160-
conversions have been performed, so the type of the objects in the *choices*
1161-
sequence should match the type_ specified.
1162-
11631162
Any sequence can be passed as the *choices* value, so :class:`list` objects,
11641163
:class:`tuple` objects, and custom sequences are all supported.
11651164

@@ -1172,6 +1171,36 @@ from *dest*. This is usually what you want because the user never sees the
11721171
many choices), just specify an explicit metavar_.
11731172

11741173

1174+
.. _convert_choices:
1175+
1176+
convert_choices
1177+
^^^^^^^^^^^^^^^
1178+
1179+
By default, when a user passes both a ``type`` and a ``choices`` argument, the
1180+
``choices`` need to be specified in the target type, after conversion.
1181+
This can cause confusing ``usage`` and ``help`` strings.
1182+
To specify ``choices`` before conversion, set the flag ``convert_choices``::
1183+
1184+
>>> def to_dow(s):
1185+
... return ['mo', 'tu', 'we', 'th', 'fr', 'sa', 'su'].index(x)
1186+
...
1187+
>>> parser = argparse.ArgumentParser()
1188+
>>> parser.add_argument('when',
1189+
... choices=['mo', 'tu', 'we', 'th', 'fr', 'sa', 'su'],
1190+
... convert_choices=True,
1191+
... type=to_dow)
1192+
>>> parser.print_help()
1193+
usage: sphinx-build [-h] {mo,tu,we,th,fr,sa,su}
1194+
1195+
positional arguments:
1196+
{mo,tu,we,th,fr,sa,su}
1197+
1198+
options:
1199+
-h, --help show this help message and exit
1200+
1201+
.. versionadded:: next
1202+
1203+
11751204
.. _required:
11761205

11771206
required

Doc/whatsnew/3.15.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,9 @@ New features
7575
Other language changes
7676
======================
7777

78+
* Several error messages incorrectly using the term "argument" have been corrected.
79+
(Contributed by Stan Ulbrych in :gh:`133382`.)
80+
7881

7982

8083
New modules

Grammar/python.gram

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1305,7 +1305,7 @@ invalid_dict_comprehension:
13051305
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "dict unpacking cannot be used in dict comprehension") }
13061306
invalid_parameters:
13071307
| a="/" ',' {
1308-
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "at least one argument must precede /") }
1308+
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "at least one parameter must precede /") }
13091309
| (slash_no_default | slash_with_default) param_maybe_default* a='/' {
13101310
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "/ may appear only once") }
13111311
| slash_no_default? param_no_default* invalid_parameters_helper a=param_no_default {
@@ -1319,21 +1319,21 @@ invalid_parameters:
13191319
invalid_default:
13201320
| a='=' &(')'|',') { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "expected default value expression") }
13211321
invalid_star_etc:
1322-
| a='*' (')' | ',' (')' | '**')) { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "named arguments must follow bare *") }
1322+
| a='*' (')' | ',' (')' | '**')) { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "named parameters must follow bare *") }
13231323
| '*' ',' TYPE_COMMENT { RAISE_SYNTAX_ERROR("bare * has associated type comment") }
1324-
| '*' param a='=' { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "var-positional argument cannot have default value") }
1324+
| '*' param a='=' { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "var-positional parameter cannot have default value") }
13251325
| '*' (param_no_default | ',') param_maybe_default* a='*' (param_no_default | ',') {
1326-
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "* argument may appear only once") }
1326+
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "* may appear only once") }
13271327
invalid_kwds:
1328-
| '**' param a='=' { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "var-keyword argument cannot have default value") }
1329-
| '**' param ',' a=param { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "arguments cannot follow var-keyword argument") }
1330-
| '**' param ',' a[Token*]=('*'|'**'|'/') { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "arguments cannot follow var-keyword argument") }
1328+
| '**' param a='=' { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "var-keyword parameter cannot have default value") }
1329+
| '**' param ',' a=param { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "parameters cannot follow var-keyword parameter") }
1330+
| '**' param ',' a[Token*]=('*'|'**'|'/') { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "parameters cannot follow var-keyword parameter") }
13311331
invalid_parameters_helper: # This is only there to avoid type errors
13321332
| a=slash_with_default { _PyPegen_singleton_seq(p, a) }
13331333
| param_with_default+
13341334
invalid_lambda_parameters:
13351335
| a="/" ',' {
1336-
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "at least one argument must precede /") }
1336+
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "at least one parameter must precede /") }
13371337
| (lambda_slash_no_default | lambda_slash_with_default) lambda_param_maybe_default* a='/' {
13381338
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "/ may appear only once") }
13391339
| lambda_slash_no_default? lambda_param_no_default* invalid_lambda_parameters_helper a=lambda_param_no_default {
@@ -1348,14 +1348,14 @@ invalid_lambda_parameters_helper:
13481348
| a=lambda_slash_with_default { _PyPegen_singleton_seq(p, a) }
13491349
| lambda_param_with_default+
13501350
invalid_lambda_star_etc:
1351-
| '*' (':' | ',' (':' | '**')) { RAISE_SYNTAX_ERROR("named arguments must follow bare *") }
1352-
| '*' lambda_param a='=' { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "var-positional argument cannot have default value") }
1351+
| '*' (':' | ',' (':' | '**')) { RAISE_SYNTAX_ERROR("named parameters must follow bare *") }
1352+
| '*' lambda_param a='=' { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "var-positional parameter cannot have default value") }
13531353
| '*' (lambda_param_no_default | ',') lambda_param_maybe_default* a='*' (lambda_param_no_default | ',') {
1354-
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "* argument may appear only once") }
1354+
RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "* may appear only once") }
13551355
invalid_lambda_kwds:
1356-
| '**' lambda_param a='=' { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "var-keyword argument cannot have default value") }
1357-
| '**' lambda_param ',' a=lambda_param { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "arguments cannot follow var-keyword argument") }
1358-
| '**' lambda_param ',' a[Token*]=('*'|'**'|'/') { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "arguments cannot follow var-keyword argument") }
1356+
| '**' lambda_param a='=' { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "var-keyword parameter cannot have default value") }
1357+
| '**' lambda_param ',' a=lambda_param { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "parameters cannot follow var-keyword parameter") }
1358+
| '**' lambda_param ',' a[Token*]=('*'|'**'|'/') { RAISE_SYNTAX_ERROR_KNOWN_LOCATION(a, "parameters cannot follow var-keyword parameter") }
13591359
invalid_double_type_comments:
13601360
| TYPE_COMMENT NEWLINE TYPE_COMMENT NEWLINE INDENT {
13611361
RAISE_SYNTAX_ERROR("Cannot have two type comments on def") }

Lib/argparse.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -875,6 +875,9 @@ class Action(_AttributeHolder):
875875
type, an exception will be raised if it is not a member of this
876876
collection.
877877
878+
- convert_choices - Runs the ``choices`` through the ``type`` callable
879+
during checking. (default: ``False``)
880+
878881
- required -- True if the action must always be specified at the
879882
command line. This is only meaningful for optional command-line
880883
arguments.
@@ -893,6 +896,7 @@ def __init__(self,
893896
default=None,
894897
type=None,
895898
choices=None,
899+
convert_choices=False,
896900
required=False,
897901
help=None,
898902
metavar=None,
@@ -904,6 +908,7 @@ def __init__(self,
904908
self.default = default
905909
self.type = type
906910
self.choices = choices
911+
self.convert_choices = convert_choices
907912
self.required = required
908913
self.help = help
909914
self.metavar = metavar
@@ -918,6 +923,7 @@ def _get_kwargs(self):
918923
'default',
919924
'type',
920925
'choices',
926+
'convert_choices',
921927
'required',
922928
'help',
923929
'metavar',
@@ -980,6 +986,7 @@ def __init__(self,
980986
default=None,
981987
type=None,
982988
choices=None,
989+
convert_choices=False,
983990
required=False,
984991
help=None,
985992
metavar=None,
@@ -998,6 +1005,7 @@ def __init__(self,
9981005
default=default,
9991006
type=type,
10001007
choices=choices,
1008+
convert_choices=convert_choices,
10011009
required=required,
10021010
help=help,
10031011
metavar=metavar,
@@ -2678,9 +2686,8 @@ def _check_value(self, action, value, arg_string=None):
26782686
choices = iter(choices)
26792687

26802688
typed_choices = []
2681-
if (self.convert_choices and
2682-
action.type and
2683-
all(isinstance(choice, str) for choice in choices)
2689+
if (action.convert_choices and
2690+
action.type
26842691
):
26852692
typed_choices = [action.type(v) for v in choices]
26862693

Lib/test/test_argparse.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1958,10 +1958,11 @@ def to_dow(arg):
19581958
class TestTypedChoices(TestChoices):
19591959
"""Test a set of string choices that convert to weekdays"""
19601960

1961-
parser_signature = Sig(convert_choices=True)
19621961
argument_signatures = [
19631962
Sig('when',
1964-
type=TestChoices.to_dow, choices=["mo", "tu", "we" , "th", "fr", "sa", "su"],
1963+
type=TestChoices.to_dow,
1964+
choices=["mo", "tu", "we" , "th", "fr", "sa", "su"],
1965+
convert_choices=True,
19651966
)
19661967
]
19671968

@@ -5519,11 +5520,12 @@ def to_date(arg):
55195520
else:
55205521
return None
55215522

5522-
parser_signature = Sig(prog='PROG', convert_choices=True)
5523+
parser_signature = Sig(prog='PROG')
55235524
argument_signatures = [
55245525
Sig('when',
55255526
type=to_date,
5526-
choices=["today", "tomorrow"]
5527+
choices=["today", "tomorrow"],
5528+
convert_choices=True
55275529
),
55285530
]
55295531

@@ -5933,7 +5935,8 @@ def test_optional(self):
59335935
string = (
59345936
"Action(option_strings=['--foo', '-a', '-b'], dest='b', "
59355937
"nargs='+', const=None, default=42, type='int', "
5936-
"choices=[1, 2, 3], required=False, help='HELP', "
5938+
"choices=[1, 2, 3], convert_choices=False, "
5939+
"required=False, help='HELP', "
59375940
"metavar='METAVAR', deprecated=False)")
59385941
self.assertStringEqual(option, string)
59395942

@@ -5951,6 +5954,7 @@ def test_argument(self):
59515954
string = (
59525955
"Action(option_strings=[], dest='x', nargs='?', "
59535956
"const=None, default=2.5, type=%r, choices=[0.5, 1.5, 2.5], "
5957+
"convert_choices=False, "
59545958
"required=True, help='H HH H', metavar='MV MV MV', "
59555959
"deprecated=False)" % float)
59565960
self.assertStringEqual(argument, string)

Lib/test/test_codeop.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -322,7 +322,7 @@ def test_syntax_errors(self):
322322
dedent("""\
323323
def foo(x,x):
324324
pass
325-
"""), "duplicate argument 'x' in function definition")
325+
"""), "duplicate parameter 'x' in function definition")
326326

327327

328328

Lib/test/test_positional_only_arg.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ def test_invalid_syntax_errors(self):
3737
check_syntax_error(self, "def f(/): pass")
3838
check_syntax_error(self, "def f(*, a, /): pass")
3939
check_syntax_error(self, "def f(*, /, a): pass")
40-
check_syntax_error(self, "def f(a, /, a): pass", "duplicate argument 'a' in function definition")
41-
check_syntax_error(self, "def f(a, /, *, a): pass", "duplicate argument 'a' in function definition")
40+
check_syntax_error(self, "def f(a, /, a): pass", "duplicate parameter 'a' in function definition")
41+
check_syntax_error(self, "def f(a, /, *, a): pass", "duplicate parameter 'a' in function definition")
4242
check_syntax_error(self, "def f(a, b/2, c): pass")
4343
check_syntax_error(self, "def f(a, /, c, /): pass")
4444
check_syntax_error(self, "def f(a, /, c, /, d): pass")
@@ -59,8 +59,8 @@ def test_invalid_syntax_errors_async(self):
5959
check_syntax_error(self, "async def f(/): pass")
6060
check_syntax_error(self, "async def f(*, a, /): pass")
6161
check_syntax_error(self, "async def f(*, /, a): pass")
62-
check_syntax_error(self, "async def f(a, /, a): pass", "duplicate argument 'a' in function definition")
63-
check_syntax_error(self, "async def f(a, /, *, a): pass", "duplicate argument 'a' in function definition")
62+
check_syntax_error(self, "async def f(a, /, a): pass", "duplicate parameter 'a' in function definition")
63+
check_syntax_error(self, "async def f(a, /, *, a): pass", "duplicate parameter 'a' in function definition")
6464
check_syntax_error(self, "async def f(a, b/2, c): pass")
6565
check_syntax_error(self, "async def f(a, /, c, /): pass")
6666
check_syntax_error(self, "async def f(a, /, c, /, d): pass")
@@ -247,8 +247,8 @@ def test_invalid_syntax_lambda(self):
247247
check_syntax_error(self, "lambda /: None")
248248
check_syntax_error(self, "lambda *, a, /: None")
249249
check_syntax_error(self, "lambda *, /, a: None")
250-
check_syntax_error(self, "lambda a, /, a: None", "duplicate argument 'a' in function definition")
251-
check_syntax_error(self, "lambda a, /, *, a: None", "duplicate argument 'a' in function definition")
250+
check_syntax_error(self, "lambda a, /, a: None", "duplicate parameter 'a' in function definition")
251+
check_syntax_error(self, "lambda a, /, *, a: None", "duplicate parameter 'a' in function definition")
252252
check_syntax_error(self, "lambda a, /, b, /: None")
253253
check_syntax_error(self, "lambda a, /, b, /, c: None")
254254
check_syntax_error(self, "lambda a, /, b, /, c, *, d: None")

Lib/test/test_pyrepl/test_interact.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ def test_runsource_show_syntax_error_location(self):
113113
r = """
114114
def f(x, x): ...
115115
^
116-
SyntaxError: duplicate argument 'x' in function definition"""
116+
SyntaxError: duplicate parameter 'x' in function definition"""
117117
self.assertIn(r, f.getvalue())
118118

119119
def test_runsource_shows_syntax_error_for_failed_compilation(self):

Lib/test/test_repl.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ def test_runsource_show_syntax_error_location(self):
197197
expected_lines = [
198198
' def f(x, x): ...',
199199
' ^',
200-
"SyntaxError: duplicate argument 'x' in function definition"
200+
"SyntaxError: duplicate parameter 'x' in function definition"
201201
]
202202
self.assertEqual(output.splitlines()[4:-1], expected_lines)
203203

0 commit comments

Comments
 (0)
0