Skip to content

Commit dbe1665

Browse files
committed
Merge branch 'main' into superopt
* main: gh-100227: Only Use deepfreeze for the Main Interpreter (gh-103794) gh-103492: Clarify SyntaxWarning with literal comparison (#103493) gh-101100: Fix Sphinx warnings in `argparse` module (#103289)
2 parents 0de5bc6 + 19e4f75 commit dbe1665

File tree

12 files changed

+170
-66
lines changed

12 files changed

+170
-66
lines changed

Doc/conf.py

+7
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,13 @@
7676
if venvdir is not None:
7777
exclude_patterns.append(venvdir + '/*')
7878

79+
nitpick_ignore = [
80+
# Do not error nit-picky mode builds when _SubParsersAction.add_parser cannot
81+
# be resolved, as the method is currently undocumented. For context, see
82+
# https://github.com/python/cpython/pull/103289.
83+
('py:meth', '_SubParsersAction.add_parser'),
84+
]
85+
7986
# Disable Docutils smartquotes for several translations
8087
smartquotes_excludes = {
8188
'languages': ['ja', 'fr', 'zh_TW', 'zh_CN'], 'builders': ['man', 'text'],

Doc/howto/argparse.rst

+9-7
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
1+
.. _argparse-tutorial:
2+
13
*****************
24
Argparse Tutorial
35
*****************
46

57
:author: Tshepang Mbambo
68

7-
.. _argparse-tutorial:
9+
.. currentmodule:: argparse
810

911
This tutorial is intended to be a gentle introduction to :mod:`argparse`, the
1012
recommended command-line parsing module in the Python standard library.
1113

1214
.. note::
1315

1416
There are two other modules that fulfill the same task, namely
15-
:mod:`getopt` (an equivalent for :c:func:`getopt` from the C
17+
:mod:`getopt` (an equivalent for ``getopt()`` from the C
1618
language) and the deprecated :mod:`optparse`.
1719
Note also that :mod:`argparse` is based on :mod:`optparse`,
1820
and therefore very similar in terms of usage.
@@ -137,13 +139,13 @@ And running the code:
137139
138140
Here is what's happening:
139141

140-
* We've added the :meth:`add_argument` method, which is what we use to specify
142+
* We've added the :meth:`~ArgumentParser.add_argument` method, which is what we use to specify
141143
which command-line options the program is willing to accept. In this case,
142144
I've named it ``echo`` so that it's in line with its function.
143145

144146
* Calling our program now requires us to specify an option.
145147

146-
* The :meth:`parse_args` method actually returns some data from the
148+
* The :meth:`~ArgumentParser.parse_args` method actually returns some data from the
147149
options specified, in this case, ``echo``.
148150

149151
* The variable is some form of 'magic' that :mod:`argparse` performs for free
@@ -256,7 +258,7 @@ Here is what is happening:
256258

257259
* To show that the option is actually optional, there is no error when running
258260
the program without it. Note that by default, if an optional argument isn't
259-
used, the relevant variable, in this case :attr:`args.verbosity`, is
261+
used, the relevant variable, in this case ``args.verbosity``, is
260262
given ``None`` as a value, which is the reason it fails the truth
261263
test of the :keyword:`if` statement.
262264

@@ -299,7 +301,7 @@ Here is what is happening:
299301
We even changed the name of the option to match that idea.
300302
Note that we now specify a new keyword, ``action``, and give it the value
301303
``"store_true"``. This means that, if the option is specified,
302-
assign the value ``True`` to :data:`args.verbose`.
304+
assign the value ``True`` to ``args.verbose``.
303305
Not specifying it implies ``False``.
304306

305307
* It complains when you specify a value, in true spirit of what flags
@@ -698,7 +700,7 @@ Conflicting options
698700

699701
So far, we have been working with two methods of an
700702
:class:`argparse.ArgumentParser` instance. Let's introduce a third one,
701-
:meth:`add_mutually_exclusive_group`. It allows for us to specify options that
703+
:meth:`~ArgumentParser.add_mutually_exclusive_group`. It allows for us to specify options that
702704
conflict with each other. Let's also change the rest of the program so that
703705
the new functionality makes more sense:
704706
we'll introduce the ``--quiet`` option,

Doc/library/argparse.rst

+20-6
Original file line numberDiff line numberDiff line change
@@ -585,7 +585,7 @@ arguments will never be treated as file references.
585585

586586
.. versionchanged:: 3.12
587587
:class:`ArgumentParser` changed encoding and errors to read arguments files
588-
from default (e.g. :func:`locale.getpreferredencoding(False)` and
588+
from default (e.g. :func:`locale.getpreferredencoding(False) <locale.getpreferredencoding>` and
589589
``"strict"``) to :term:`filesystem encoding and error handler`.
590590
Arguments file should be encoded in UTF-8 instead of ANSI Codepage on Windows.
591591

@@ -1191,7 +1191,7 @@ done downstream after the arguments are parsed.
11911191
For example, JSON or YAML conversions have complex error cases that require
11921192
better reporting than can be given by the ``type`` keyword. A
11931193
:exc:`~json.JSONDecodeError` would not be well formatted and a
1194-
:exc:`FileNotFound` exception would not be handled at all.
1194+
:exc:`FileNotFoundError` exception would not be handled at all.
11951195

11961196
Even :class:`~argparse.FileType` has its limitations for use with the ``type``
11971197
keyword. If one argument uses *FileType* and then a subsequent argument fails,
@@ -1445,7 +1445,7 @@ Action classes
14451445
Action classes implement the Action API, a callable which returns a callable
14461446
which processes arguments from the command-line. Any object which follows
14471447
this API may be passed as the ``action`` parameter to
1448-
:meth:`add_argument`.
1448+
:meth:`~ArgumentParser.add_argument`.
14491449

14501450
.. class:: Action(option_strings, dest, nargs=None, const=None, default=None, \
14511451
type=None, choices=None, required=False, help=None, \
@@ -1723,7 +1723,7 @@ Sub-commands
17231723
:class:`ArgumentParser` supports the creation of such sub-commands with the
17241724
:meth:`add_subparsers` method. The :meth:`add_subparsers` method is normally
17251725
called with no arguments and returns a special action object. This object
1726-
has a single method, :meth:`~ArgumentParser.add_parser`, which takes a
1726+
has a single method, :meth:`~_SubParsersAction.add_parser`, which takes a
17271727
command name and any :class:`ArgumentParser` constructor arguments, and
17281728
returns an :class:`ArgumentParser` object that can be modified as usual.
17291729

@@ -1789,7 +1789,7 @@ Sub-commands
17891789
for that particular parser will be printed. The help message will not
17901790
include parent parser or sibling parser messages. (A help message for each
17911791
subparser command, however, can be given by supplying the ``help=`` argument
1792-
to :meth:`add_parser` as above.)
1792+
to :meth:`~_SubParsersAction.add_parser` as above.)
17931793

17941794
::
17951795

@@ -2157,7 +2157,7 @@ the populated namespace and the list of remaining argument strings.
21572157

21582158
.. warning::
21592159
:ref:`Prefix matching <prefix-matching>` rules apply to
2160-
:meth:`parse_known_args`. The parser may consume an option even if it's just
2160+
:meth:`~ArgumentParser.parse_known_args`. The parser may consume an option even if it's just
21612161
a prefix of one of its known options, instead of leaving it in the remaining
21622162
arguments list.
21632163

@@ -2295,3 +2295,17 @@ A partial upgrade path from :mod:`optparse` to :mod:`argparse`:
22952295

22962296
* Replace the OptionParser constructor ``version`` argument with a call to
22972297
``parser.add_argument('--version', action='version', version='<the version>')``.
2298+
2299+
Exceptions
2300+
----------
2301+
2302+
.. exception:: ArgumentError
2303+
2304+
An error from creating or using an argument (optional or positional).
2305+
2306+
The string value of this exception is the message, augmented with
2307+
information about the argument that caused it.
2308+
2309+
.. exception:: ArgumentTypeError
2310+
2311+
Raised when something goes wrong converting a command line string to a type.

Doc/library/optparse.rst

+42-1
Original file line numberDiff line numberDiff line change
@@ -954,7 +954,16 @@ The canonical way to create an :class:`Option` instance is with the
954954

955955
As you can see, most actions involve storing or updating a value somewhere.
956956
:mod:`optparse` always creates a special object for this, conventionally called
957-
``options`` (it happens to be an instance of :class:`optparse.Values`). Option
957+
``options``, which is an instance of :class:`optparse.Values`.
958+
959+
.. class:: Values
960+
961+
An object holding parsed argument names and values as attributes.
962+
Normally created by calling when calling :meth:`OptionParser.parse_args`,
963+
and can be overridden by a custom subclass passed to the *values* argument of
964+
:meth:`OptionParser.parse_args` (as described in :ref:`optparse-parsing-arguments`).
965+
966+
Option
958967
arguments (and various other values) are stored as attributes of this object,
959968
according to the :attr:`~Option.dest` (destination) option attribute.
960969

@@ -991,6 +1000,14 @@ one that makes sense for *all* options.
9911000
Option attributes
9921001
^^^^^^^^^^^^^^^^^
9931002

1003+
.. class:: Option
1004+
1005+
A single command line argument,
1006+
with various attributes passed by keyword to the constructor.
1007+
Normally created with :meth:`OptionParser.add_option` rather than directly,
1008+
and can be overridden by a custom class via the *option_class* argument
1009+
to :class:`OptionParser`.
1010+
9941011
The following option attributes may be passed as keyword arguments to
9951012
:meth:`OptionParser.add_option`. If you pass an option attribute that is not
9961013
relevant to a particular option, or fail to pass a required option attribute,
@@ -2035,3 +2052,27 @@ Features of note:
20352052
about setting a default value for the option destinations in question; they
20362053
can just leave the default as ``None`` and :meth:`ensure_value` will take care of
20372054
getting it right when it's needed.
2055+
2056+
Exceptions
2057+
----------
2058+
2059+
.. exception:: OptionError
2060+
2061+
Raised if an :class:`Option` instance is created with invalid or
2062+
inconsistent arguments.
2063+
2064+
.. exception:: OptionConflictError
2065+
2066+
Raised if conflicting options are added to an :class:`OptionParser`.
2067+
2068+
.. exception:: OptionValueError
2069+
2070+
Raised if an invalid option value is encountered on the command line.
2071+
2072+
.. exception:: BadOptionError
2073+
2074+
Raised if an invalid option is passed on the command line.
2075+
2076+
.. exception:: AmbiguousOptionError
2077+
2078+
Raised if an ambiguous option is passed on the command line.

Doc/tools/.nitignore

-2
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,6 @@ Doc/faq/gui.rst
5959
Doc/faq/library.rst
6060
Doc/faq/programming.rst
6161
Doc/glossary.rst
62-
Doc/howto/argparse.rst
6362
Doc/howto/curses.rst
6463
Doc/howto/descriptor.rst
6564
Doc/howto/enum.rst
@@ -78,7 +77,6 @@ Doc/library/__future__.rst
7877
Doc/library/_thread.rst
7978
Doc/library/abc.rst
8079
Doc/library/aifc.rst
81-
Doc/library/argparse.rst
8280
Doc/library/ast.rst
8381
Doc/library/asyncio-dev.rst
8482
Doc/library/asyncio-eventloop.rst

Lib/test/test_codeop.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -277,7 +277,7 @@ def test_filename(self):
277277
def test_warning(self):
278278
# Test that the warning is only returned once.
279279
with warnings_helper.check_warnings(
280-
('"is" with a literal', SyntaxWarning),
280+
('"is" with \'str\' literal', SyntaxWarning),
281281
("invalid escape sequence", SyntaxWarning),
282282
) as w:
283283
compile_command(r"'\e' is 0")

Lib/test/test_grammar.py

+20-11
Original file line numberDiff line numberDiff line change
@@ -236,12 +236,9 @@ def check(test, error=False):
236236
check(f"[{num}for x in ()]")
237237
check(f"{num}spam", error=True)
238238

239+
with self.assertWarnsRegex(SyntaxWarning, r'invalid \w+ literal'):
240+
compile(f"{num}is x", "<testcase>", "eval")
239241
with warnings.catch_warnings():
240-
warnings.filterwarnings('ignore', '"is" with a literal',
241-
SyntaxWarning)
242-
with self.assertWarnsRegex(SyntaxWarning,
243-
r'invalid \w+ literal'):
244-
compile(f"{num}is x", "<testcase>", "eval")
245242
warnings.simplefilter('error', SyntaxWarning)
246243
with self.assertRaisesRegex(SyntaxError,
247244
r'invalid \w+ literal'):
@@ -1467,21 +1464,33 @@ def test_comparison(self):
14671464
if 1 < 1 > 1 == 1 >= 1 <= 1 != 1 in 1 not in x is x is not x: pass
14681465

14691466
def test_comparison_is_literal(self):
1470-
def check(test, msg='"is" with a literal'):
1467+
def check(test, msg):
14711468
self.check_syntax_warning(test, msg)
14721469

1473-
check('x is 1')
1474-
check('x is "thing"')
1475-
check('1 is x')
1476-
check('x is y is 1')
1477-
check('x is not 1', '"is not" with a literal')
1470+
check('x is 1', '"is" with \'int\' literal')
1471+
check('x is "thing"', '"is" with \'str\' literal')
1472+
check('1 is x', '"is" with \'int\' literal')
1473+
check('x is y is 1', '"is" with \'int\' literal')
1474+
check('x is not 1', '"is not" with \'int\' literal')
1475+
check('x is not (1, 2)', '"is not" with \'tuple\' literal')
1476+
check('(1, 2) is not x', '"is not" with \'tuple\' literal')
1477+
1478+
check('None is 1', '"is" with \'int\' literal')
1479+
check('1 is None', '"is" with \'int\' literal')
1480+
1481+
check('x == 3 is y', '"is" with \'int\' literal')
1482+
check('x == "thing" is y', '"is" with \'str\' literal')
14781483

14791484
with warnings.catch_warnings():
14801485
warnings.simplefilter('error', SyntaxWarning)
14811486
compile('x is None', '<testcase>', 'exec')
14821487
compile('x is False', '<testcase>', 'exec')
14831488
compile('x is True', '<testcase>', 'exec')
14841489
compile('x is ...', '<testcase>', 'exec')
1490+
compile('None is x', '<testcase>', 'exec')
1491+
compile('False is x', '<testcase>', 'exec')
1492+
compile('True is x', '<testcase>', 'exec')
1493+
compile('... is x', '<testcase>', 'exec')
14851494

14861495
def test_warn_missed_comma(self):
14871496
def check(test):

Makefile.pre.in

+1-1
Original file line numberDiff line numberDiff line change
@@ -1194,7 +1194,7 @@ Tools/build/freeze_modules.py: $(FREEZE_MODULE)
11941194

11951195
.PHONY: regen-frozen
11961196
regen-frozen: Tools/build/freeze_modules.py $(FROZEN_FILES_IN)
1197-
$(PYTHON_FOR_REGEN) $(srcdir)/Tools/build/freeze_modules.py
1197+
$(PYTHON_FOR_REGEN) $(srcdir)/Tools/build/freeze_modules.py --frozen-modules
11981198
@echo "The Makefile was updated, you may need to re-run make."
11991199

12001200
############################################################################
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Clarify :exc:`SyntaxWarning` with literal ``is`` comparison by specifying which literal is problematic, since comparisons using ``is`` with e.g. None and bool literals are idiomatic.

Python/compile.c

+12-4
Original file line numberDiff line numberDiff line change
@@ -2291,6 +2291,8 @@ check_is_arg(expr_ty e)
22912291
|| value == Py_Ellipsis);
22922292
}
22932293

2294+
static PyTypeObject * infer_type(expr_ty e);
2295+
22942296
/* Check operands of identity checks ("is" and "is not").
22952297
Emit a warning if any operand is a constant except named singletons.
22962298
*/
@@ -2299,19 +2301,25 @@ check_compare(struct compiler *c, expr_ty e)
22992301
{
23002302
Py_ssize_t i, n;
23012303
bool left = check_is_arg(e->v.Compare.left);
2304+
expr_ty left_expr = e->v.Compare.left;
23022305
n = asdl_seq_LEN(e->v.Compare.ops);
23032306
for (i = 0; i < n; i++) {
23042307
cmpop_ty op = (cmpop_ty)asdl_seq_GET(e->v.Compare.ops, i);
2305-
bool right = check_is_arg((expr_ty)asdl_seq_GET(e->v.Compare.comparators, i));
2308+
expr_ty right_expr = (expr_ty)asdl_seq_GET(e->v.Compare.comparators, i);
2309+
bool right = check_is_arg(right_expr);
23062310
if (op == Is || op == IsNot) {
23072311
if (!right || !left) {
23082312
const char *msg = (op == Is)
2309-
? "\"is\" with a literal. Did you mean \"==\"?"
2310-
: "\"is not\" with a literal. Did you mean \"!=\"?";
2311-
return compiler_warn(c, LOC(e), msg);
2313+
? "\"is\" with '%.200s' literal. Did you mean \"==\"?"
2314+
: "\"is not\" with '%.200s' literal. Did you mean \"!=\"?";
2315+
expr_ty literal = !left ? left_expr : right_expr;
2316+
return compiler_warn(
2317+
c, LOC(e), msg, infer_type(literal)->tp_name
2318+
);
23122319
}
23132320
}
23142321
left = right;
2322+
left_expr = right_expr;
23152323
}
23162324
return SUCCESS;
23172325
}

0 commit comments

Comments
 (0)