From 79ae019164eeb6b94118bc17bc1e937405684c75 Mon Sep 17 00:00:00 2001 From: Hugo van Kemenade Date: Mon, 24 Apr 2023 15:36:32 -0600 Subject: [PATCH 1/3] gh-101100: Fix Sphinx warnings in `argparse` module (#103289) Co-authored-by: C.A.M. Gerlach Co-authored-by: Ezio Melotti --- Doc/conf.py | 7 +++++++ Doc/howto/argparse.rst | 16 ++++++++------- Doc/library/argparse.rst | 26 ++++++++++++++++++------ Doc/library/optparse.rst | 43 +++++++++++++++++++++++++++++++++++++++- Doc/tools/.nitignore | 2 -- 5 files changed, 78 insertions(+), 16 deletions(-) diff --git a/Doc/conf.py b/Doc/conf.py index 4c120bee64dde4..42c23bf77c7034 100644 --- a/Doc/conf.py +++ b/Doc/conf.py @@ -76,6 +76,13 @@ if venvdir is not None: exclude_patterns.append(venvdir + '/*') +nitpick_ignore = [ + # Do not error nit-picky mode builds when _SubParsersAction.add_parser cannot + # be resolved, as the method is currently undocumented. For context, see + # https://github.com/python/cpython/pull/103289. + ('py:meth', '_SubParsersAction.add_parser'), +] + # Disable Docutils smartquotes for several translations smartquotes_excludes = { 'languages': ['ja', 'fr', 'zh_TW', 'zh_CN'], 'builders': ['man', 'text'], diff --git a/Doc/howto/argparse.rst b/Doc/howto/argparse.rst index f682587488a227..52e98fa9620194 100644 --- a/Doc/howto/argparse.rst +++ b/Doc/howto/argparse.rst @@ -1,10 +1,12 @@ +.. _argparse-tutorial: + ***************** Argparse Tutorial ***************** :author: Tshepang Mbambo -.. _argparse-tutorial: +.. currentmodule:: argparse This tutorial is intended to be a gentle introduction to :mod:`argparse`, the recommended command-line parsing module in the Python standard library. @@ -12,7 +14,7 @@ recommended command-line parsing module in the Python standard library. .. note:: There are two other modules that fulfill the same task, namely - :mod:`getopt` (an equivalent for :c:func:`getopt` from the C + :mod:`getopt` (an equivalent for ``getopt()`` from the C language) and the deprecated :mod:`optparse`. Note also that :mod:`argparse` is based on :mod:`optparse`, and therefore very similar in terms of usage. @@ -137,13 +139,13 @@ And running the code: Here is what's happening: -* We've added the :meth:`add_argument` method, which is what we use to specify +* We've added the :meth:`~ArgumentParser.add_argument` method, which is what we use to specify which command-line options the program is willing to accept. In this case, I've named it ``echo`` so that it's in line with its function. * Calling our program now requires us to specify an option. -* The :meth:`parse_args` method actually returns some data from the +* The :meth:`~ArgumentParser.parse_args` method actually returns some data from the options specified, in this case, ``echo``. * The variable is some form of 'magic' that :mod:`argparse` performs for free @@ -256,7 +258,7 @@ Here is what is happening: * To show that the option is actually optional, there is no error when running the program without it. Note that by default, if an optional argument isn't - used, the relevant variable, in this case :attr:`args.verbosity`, is + used, the relevant variable, in this case ``args.verbosity``, is given ``None`` as a value, which is the reason it fails the truth test of the :keyword:`if` statement. @@ -299,7 +301,7 @@ Here is what is happening: We even changed the name of the option to match that idea. Note that we now specify a new keyword, ``action``, and give it the value ``"store_true"``. This means that, if the option is specified, - assign the value ``True`` to :data:`args.verbose`. + assign the value ``True`` to ``args.verbose``. Not specifying it implies ``False``. * It complains when you specify a value, in true spirit of what flags @@ -698,7 +700,7 @@ Conflicting options So far, we have been working with two methods of an :class:`argparse.ArgumentParser` instance. Let's introduce a third one, -:meth:`add_mutually_exclusive_group`. It allows for us to specify options that +:meth:`~ArgumentParser.add_mutually_exclusive_group`. It allows for us to specify options that conflict with each other. Let's also change the rest of the program so that the new functionality makes more sense: we'll introduce the ``--quiet`` option, diff --git a/Doc/library/argparse.rst b/Doc/library/argparse.rst index dd59181a30702e..33e367f3ccda89 100644 --- a/Doc/library/argparse.rst +++ b/Doc/library/argparse.rst @@ -585,7 +585,7 @@ arguments will never be treated as file references. .. versionchanged:: 3.12 :class:`ArgumentParser` changed encoding and errors to read arguments files - from default (e.g. :func:`locale.getpreferredencoding(False)` and + from default (e.g. :func:`locale.getpreferredencoding(False) ` and ``"strict"``) to :term:`filesystem encoding and error handler`. Arguments file should be encoded in UTF-8 instead of ANSI Codepage on Windows. @@ -1191,7 +1191,7 @@ done downstream after the arguments are parsed. For example, JSON or YAML conversions have complex error cases that require better reporting than can be given by the ``type`` keyword. A :exc:`~json.JSONDecodeError` would not be well formatted and a -:exc:`FileNotFound` exception would not be handled at all. +:exc:`FileNotFoundError` exception would not be handled at all. Even :class:`~argparse.FileType` has its limitations for use with the ``type`` keyword. If one argument uses *FileType* and then a subsequent argument fails, @@ -1445,7 +1445,7 @@ Action classes Action classes implement the Action API, a callable which returns a callable which processes arguments from the command-line. Any object which follows this API may be passed as the ``action`` parameter to -:meth:`add_argument`. +:meth:`~ArgumentParser.add_argument`. .. class:: Action(option_strings, dest, nargs=None, const=None, default=None, \ type=None, choices=None, required=False, help=None, \ @@ -1723,7 +1723,7 @@ Sub-commands :class:`ArgumentParser` supports the creation of such sub-commands with the :meth:`add_subparsers` method. The :meth:`add_subparsers` method is normally called with no arguments and returns a special action object. This object - has a single method, :meth:`~ArgumentParser.add_parser`, which takes a + has a single method, :meth:`~_SubParsersAction.add_parser`, which takes a command name and any :class:`ArgumentParser` constructor arguments, and returns an :class:`ArgumentParser` object that can be modified as usual. @@ -1789,7 +1789,7 @@ Sub-commands for that particular parser will be printed. The help message will not include parent parser or sibling parser messages. (A help message for each subparser command, however, can be given by supplying the ``help=`` argument - to :meth:`add_parser` as above.) + to :meth:`~_SubParsersAction.add_parser` as above.) :: @@ -2157,7 +2157,7 @@ the populated namespace and the list of remaining argument strings. .. warning:: :ref:`Prefix matching ` rules apply to - :meth:`parse_known_args`. The parser may consume an option even if it's just + :meth:`~ArgumentParser.parse_known_args`. The parser may consume an option even if it's just a prefix of one of its known options, instead of leaving it in the remaining arguments list. @@ -2295,3 +2295,17 @@ A partial upgrade path from :mod:`optparse` to :mod:`argparse`: * Replace the OptionParser constructor ``version`` argument with a call to ``parser.add_argument('--version', action='version', version='')``. + +Exceptions +---------- + +.. exception:: ArgumentError + + An error from creating or using an argument (optional or positional). + + The string value of this exception is the message, augmented with + information about the argument that caused it. + +.. exception:: ArgumentTypeError + + Raised when something goes wrong converting a command line string to a type. diff --git a/Doc/library/optparse.rst b/Doc/library/optparse.rst index 468c3efbe01148..5c02d8bc8835bf 100644 --- a/Doc/library/optparse.rst +++ b/Doc/library/optparse.rst @@ -954,7 +954,16 @@ The canonical way to create an :class:`Option` instance is with the As you can see, most actions involve storing or updating a value somewhere. :mod:`optparse` always creates a special object for this, conventionally called -``options`` (it happens to be an instance of :class:`optparse.Values`). Option +``options``, which is an instance of :class:`optparse.Values`. + +.. class:: Values + + An object holding parsed argument names and values as attributes. + Normally created by calling when calling :meth:`OptionParser.parse_args`, + and can be overridden by a custom subclass passed to the *values* argument of + :meth:`OptionParser.parse_args` (as described in :ref:`optparse-parsing-arguments`). + +Option arguments (and various other values) are stored as attributes of this object, according to the :attr:`~Option.dest` (destination) option attribute. @@ -991,6 +1000,14 @@ one that makes sense for *all* options. Option attributes ^^^^^^^^^^^^^^^^^ +.. class:: Option + + A single command line argument, + with various attributes passed by keyword to the constructor. + Normally created with :meth:`OptionParser.add_option` rather than directly, + and can be overridden by a custom class via the *option_class* argument + to :class:`OptionParser`. + The following option attributes may be passed as keyword arguments to :meth:`OptionParser.add_option`. If you pass an option attribute that is not relevant to a particular option, or fail to pass a required option attribute, @@ -2035,3 +2052,27 @@ Features of note: about setting a default value for the option destinations in question; they can just leave the default as ``None`` and :meth:`ensure_value` will take care of getting it right when it's needed. + +Exceptions +---------- + +.. exception:: OptionError + + Raised if an :class:`Option` instance is created with invalid or + inconsistent arguments. + +.. exception:: OptionConflictError + + Raised if conflicting options are added to an :class:`OptionParser`. + +.. exception:: OptionValueError + + Raised if an invalid option value is encountered on the command line. + +.. exception:: BadOptionError + + Raised if an invalid option is passed on the command line. + +.. exception:: AmbiguousOptionError + + Raised if an ambiguous option is passed on the command line. diff --git a/Doc/tools/.nitignore b/Doc/tools/.nitignore index f6fe8df97810ff..e77da1c12eba1c 100644 --- a/Doc/tools/.nitignore +++ b/Doc/tools/.nitignore @@ -59,7 +59,6 @@ Doc/faq/gui.rst Doc/faq/library.rst Doc/faq/programming.rst Doc/glossary.rst -Doc/howto/argparse.rst Doc/howto/curses.rst Doc/howto/descriptor.rst Doc/howto/enum.rst @@ -78,7 +77,6 @@ Doc/library/__future__.rst Doc/library/_thread.rst Doc/library/abc.rst Doc/library/aifc.rst -Doc/library/argparse.rst Doc/library/ast.rst Doc/library/asyncio-dev.rst Doc/library/asyncio-eventloop.rst From ae25855045e8f19f4715c9b2c02cbcd81e7f6f95 Mon Sep 17 00:00:00 2001 From: Shantanu <12621235+hauntsaninja@users.noreply.github.com> Date: Mon, 24 Apr 2023 15:42:57 -0600 Subject: [PATCH 2/3] gh-103492: Clarify SyntaxWarning with literal comparison (#103493) --- Lib/test/test_codeop.py | 2 +- Lib/test/test_grammar.py | 31 ++++++++++++------- ...-04-13-00-58-55.gh-issue-103492.P4k0Ay.rst | 1 + Python/compile.c | 16 +++++++--- 4 files changed, 34 insertions(+), 16 deletions(-) create mode 100644 Misc/NEWS.d/next/Core and Builtins/2023-04-13-00-58-55.gh-issue-103492.P4k0Ay.rst diff --git a/Lib/test/test_codeop.py b/Lib/test/test_codeop.py index 6966c2ffd811b8..e3c382266fa058 100644 --- a/Lib/test/test_codeop.py +++ b/Lib/test/test_codeop.py @@ -277,7 +277,7 @@ def test_filename(self): def test_warning(self): # Test that the warning is only returned once. with warnings_helper.check_warnings( - ('"is" with a literal', SyntaxWarning), + ('"is" with \'str\' literal', SyntaxWarning), ("invalid escape sequence", SyntaxWarning), ) as w: compile_command(r"'\e' is 0") diff --git a/Lib/test/test_grammar.py b/Lib/test/test_grammar.py index ced9000f75f2e5..ee105a3de17f8a 100644 --- a/Lib/test/test_grammar.py +++ b/Lib/test/test_grammar.py @@ -236,12 +236,9 @@ def check(test, error=False): check(f"[{num}for x in ()]") check(f"{num}spam", error=True) + with self.assertWarnsRegex(SyntaxWarning, r'invalid \w+ literal'): + compile(f"{num}is x", "", "eval") with warnings.catch_warnings(): - warnings.filterwarnings('ignore', '"is" with a literal', - SyntaxWarning) - with self.assertWarnsRegex(SyntaxWarning, - r'invalid \w+ literal'): - compile(f"{num}is x", "", "eval") warnings.simplefilter('error', SyntaxWarning) with self.assertRaisesRegex(SyntaxError, r'invalid \w+ literal'): @@ -1467,14 +1464,22 @@ def test_comparison(self): if 1 < 1 > 1 == 1 >= 1 <= 1 != 1 in 1 not in x is x is not x: pass def test_comparison_is_literal(self): - def check(test, msg='"is" with a literal'): + def check(test, msg): self.check_syntax_warning(test, msg) - check('x is 1') - check('x is "thing"') - check('1 is x') - check('x is y is 1') - check('x is not 1', '"is not" with a literal') + check('x is 1', '"is" with \'int\' literal') + check('x is "thing"', '"is" with \'str\' literal') + check('1 is x', '"is" with \'int\' literal') + check('x is y is 1', '"is" with \'int\' literal') + check('x is not 1', '"is not" with \'int\' literal') + check('x is not (1, 2)', '"is not" with \'tuple\' literal') + check('(1, 2) is not x', '"is not" with \'tuple\' literal') + + check('None is 1', '"is" with \'int\' literal') + check('1 is None', '"is" with \'int\' literal') + + check('x == 3 is y', '"is" with \'int\' literal') + check('x == "thing" is y', '"is" with \'str\' literal') with warnings.catch_warnings(): warnings.simplefilter('error', SyntaxWarning) @@ -1482,6 +1487,10 @@ def check(test, msg='"is" with a literal'): compile('x is False', '', 'exec') compile('x is True', '', 'exec') compile('x is ...', '', 'exec') + compile('None is x', '', 'exec') + compile('False is x', '', 'exec') + compile('True is x', '', 'exec') + compile('... is x', '', 'exec') def test_warn_missed_comma(self): def check(test): diff --git a/Misc/NEWS.d/next/Core and Builtins/2023-04-13-00-58-55.gh-issue-103492.P4k0Ay.rst b/Misc/NEWS.d/next/Core and Builtins/2023-04-13-00-58-55.gh-issue-103492.P4k0Ay.rst new file mode 100644 index 00000000000000..929650968173e7 --- /dev/null +++ b/Misc/NEWS.d/next/Core and Builtins/2023-04-13-00-58-55.gh-issue-103492.P4k0Ay.rst @@ -0,0 +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. diff --git a/Python/compile.c b/Python/compile.c index 9603269d2b2a9d..99ef7c3a319da7 100644 --- a/Python/compile.c +++ b/Python/compile.c @@ -2269,6 +2269,8 @@ check_is_arg(expr_ty e) || value == Py_Ellipsis); } +static PyTypeObject * infer_type(expr_ty e); + /* Check operands of identity checks ("is" and "is not"). Emit a warning if any operand is a constant except named singletons. */ @@ -2277,19 +2279,25 @@ check_compare(struct compiler *c, expr_ty e) { Py_ssize_t i, n; bool left = check_is_arg(e->v.Compare.left); + expr_ty left_expr = e->v.Compare.left; n = asdl_seq_LEN(e->v.Compare.ops); for (i = 0; i < n; i++) { cmpop_ty op = (cmpop_ty)asdl_seq_GET(e->v.Compare.ops, i); - bool right = check_is_arg((expr_ty)asdl_seq_GET(e->v.Compare.comparators, i)); + expr_ty right_expr = (expr_ty)asdl_seq_GET(e->v.Compare.comparators, i); + bool right = check_is_arg(right_expr); if (op == Is || op == IsNot) { if (!right || !left) { const char *msg = (op == Is) - ? "\"is\" with a literal. Did you mean \"==\"?" - : "\"is not\" with a literal. Did you mean \"!=\"?"; - return compiler_warn(c, LOC(e), msg); + ? "\"is\" with '%.200s' literal. Did you mean \"==\"?" + : "\"is not\" with '%.200s' literal. Did you mean \"!=\"?"; + expr_ty literal = !left ? left_expr : right_expr; + return compiler_warn( + c, LOC(e), msg, infer_type(literal)->tp_name + ); } } left = right; + left_expr = right_expr; } return SUCCESS; } From 19e4f757de8c7cf2b4b9b4cbb32e376d0e50d2d4 Mon Sep 17 00:00:00 2001 From: Eric Snow Date: Mon, 24 Apr 2023 15:48:05 -0600 Subject: [PATCH 3/3] gh-100227: Only Use deepfreeze for the Main Interpreter (gh-103794) Deep-frozen code objects are cannot be shared (currently) by interpreters, due to how adaptive specialization can modify the bytecodes. We work around this by only using the deep-frozen objects in the main interpreter. This does incur a performance penalty for subinterpreters, which we may be able to resolve later. --- Makefile.pre.in | 2 +- Python/frozen.c | 81 +++++++++++++++++++++++++++++++------------------ Python/import.c | 9 +++--- 3 files changed, 58 insertions(+), 34 deletions(-) diff --git a/Makefile.pre.in b/Makefile.pre.in index b3eb00240f91e9..774a29ebf59793 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -1194,7 +1194,7 @@ Tools/build/freeze_modules.py: $(FREEZE_MODULE) .PHONY: regen-frozen regen-frozen: Tools/build/freeze_modules.py $(FROZEN_FILES_IN) - $(PYTHON_FOR_REGEN) $(srcdir)/Tools/build/freeze_modules.py + $(PYTHON_FOR_REGEN) $(srcdir)/Tools/build/freeze_modules.py --frozen-modules @echo "The Makefile was updated, you may need to re-run make." ############################################################################ diff --git a/Python/frozen.c b/Python/frozen.c index 48b429519b6606..6b977710e6e342 100644 --- a/Python/frozen.c +++ b/Python/frozen.c @@ -41,6 +41,29 @@ #include /* Includes for frozen modules: */ +#include "frozen_modules/importlib._bootstrap.h" +#include "frozen_modules/importlib._bootstrap_external.h" +#include "frozen_modules/zipimport.h" +#include "frozen_modules/abc.h" +#include "frozen_modules/codecs.h" +#include "frozen_modules/io.h" +#include "frozen_modules/_collections_abc.h" +#include "frozen_modules/_sitebuiltins.h" +#include "frozen_modules/genericpath.h" +#include "frozen_modules/ntpath.h" +#include "frozen_modules/posixpath.h" +#include "frozen_modules/os.h" +#include "frozen_modules/site.h" +#include "frozen_modules/stat.h" +#include "frozen_modules/importlib.util.h" +#include "frozen_modules/importlib.machinery.h" +#include "frozen_modules/runpy.h" +#include "frozen_modules/__hello__.h" +#include "frozen_modules/__phello__.h" +#include "frozen_modules/__phello__.ham.h" +#include "frozen_modules/__phello__.ham.eggs.h" +#include "frozen_modules/__phello__.spam.h" +#include "frozen_modules/frozen_only.h" /* End includes */ #define GET_CODE(name) _Py_get_##name##_toplevel @@ -78,46 +101,46 @@ extern PyObject *_Py_get_frozen_only_toplevel(void); /* End extern declarations */ static const struct _frozen bootstrap_modules[] = { - {"_frozen_importlib", NULL, 0, false, GET_CODE(importlib__bootstrap)}, - {"_frozen_importlib_external", NULL, 0, false, GET_CODE(importlib__bootstrap_external)}, - {"zipimport", NULL, 0, false, GET_CODE(zipimport)}, + {"_frozen_importlib", _Py_M__importlib__bootstrap, (int)sizeof(_Py_M__importlib__bootstrap), false, GET_CODE(importlib__bootstrap)}, + {"_frozen_importlib_external", _Py_M__importlib__bootstrap_external, (int)sizeof(_Py_M__importlib__bootstrap_external), false, GET_CODE(importlib__bootstrap_external)}, + {"zipimport", _Py_M__zipimport, (int)sizeof(_Py_M__zipimport), false, GET_CODE(zipimport)}, {0, 0, 0} /* bootstrap sentinel */ }; static const struct _frozen stdlib_modules[] = { /* stdlib - startup, without site (python -S) */ - {"abc", NULL, 0, false, GET_CODE(abc)}, - {"codecs", NULL, 0, false, GET_CODE(codecs)}, - {"io", NULL, 0, false, GET_CODE(io)}, + {"abc", _Py_M__abc, (int)sizeof(_Py_M__abc), false, GET_CODE(abc)}, + {"codecs", _Py_M__codecs, (int)sizeof(_Py_M__codecs), false, GET_CODE(codecs)}, + {"io", _Py_M__io, (int)sizeof(_Py_M__io), false, GET_CODE(io)}, /* stdlib - startup, with site */ - {"_collections_abc", NULL, 0, false, GET_CODE(_collections_abc)}, - {"_sitebuiltins", NULL, 0, false, GET_CODE(_sitebuiltins)}, - {"genericpath", NULL, 0, false, GET_CODE(genericpath)}, - {"ntpath", NULL, 0, false, GET_CODE(ntpath)}, - {"posixpath", NULL, 0, false, GET_CODE(posixpath)}, - {"os.path", NULL, 0, false, GET_CODE(posixpath)}, - {"os", NULL, 0, false, GET_CODE(os)}, - {"site", NULL, 0, false, GET_CODE(site)}, - {"stat", NULL, 0, false, GET_CODE(stat)}, + {"_collections_abc", _Py_M___collections_abc, (int)sizeof(_Py_M___collections_abc), false, GET_CODE(_collections_abc)}, + {"_sitebuiltins", _Py_M___sitebuiltins, (int)sizeof(_Py_M___sitebuiltins), false, GET_CODE(_sitebuiltins)}, + {"genericpath", _Py_M__genericpath, (int)sizeof(_Py_M__genericpath), false, GET_CODE(genericpath)}, + {"ntpath", _Py_M__ntpath, (int)sizeof(_Py_M__ntpath), false, GET_CODE(ntpath)}, + {"posixpath", _Py_M__posixpath, (int)sizeof(_Py_M__posixpath), false, GET_CODE(posixpath)}, + {"os.path", _Py_M__posixpath, (int)sizeof(_Py_M__posixpath), false, GET_CODE(posixpath)}, + {"os", _Py_M__os, (int)sizeof(_Py_M__os), false, GET_CODE(os)}, + {"site", _Py_M__site, (int)sizeof(_Py_M__site), false, GET_CODE(site)}, + {"stat", _Py_M__stat, (int)sizeof(_Py_M__stat), false, GET_CODE(stat)}, /* runpy - run module with -m */ - {"importlib.util", NULL, 0, false, GET_CODE(importlib_util)}, - {"importlib.machinery", NULL, 0, false, GET_CODE(importlib_machinery)}, - {"runpy", NULL, 0, false, GET_CODE(runpy)}, + {"importlib.util", _Py_M__importlib_util, (int)sizeof(_Py_M__importlib_util), false, GET_CODE(importlib_util)}, + {"importlib.machinery", _Py_M__importlib_machinery, (int)sizeof(_Py_M__importlib_machinery), false, GET_CODE(importlib_machinery)}, + {"runpy", _Py_M__runpy, (int)sizeof(_Py_M__runpy), false, GET_CODE(runpy)}, {0, 0, 0} /* stdlib sentinel */ }; static const struct _frozen test_modules[] = { - {"__hello__", NULL, 0, false, GET_CODE(__hello__)}, - {"__hello_alias__", NULL, 0, false, GET_CODE(__hello__)}, - {"__phello_alias__", NULL, 0, true, GET_CODE(__hello__)}, - {"__phello_alias__.spam", NULL, 0, false, GET_CODE(__hello__)}, - {"__phello__", NULL, 0, true, GET_CODE(__phello__)}, - {"__phello__.__init__", NULL, 0, false, GET_CODE(__phello__)}, - {"__phello__.ham", NULL, 0, true, GET_CODE(__phello___ham)}, - {"__phello__.ham.__init__", NULL, 0, false, GET_CODE(__phello___ham)}, - {"__phello__.ham.eggs", NULL, 0, false, GET_CODE(__phello___ham_eggs)}, - {"__phello__.spam", NULL, 0, false, GET_CODE(__phello___spam)}, - {"__hello_only__", NULL, 0, false, GET_CODE(frozen_only)}, + {"__hello__", _Py_M____hello__, (int)sizeof(_Py_M____hello__), false, GET_CODE(__hello__)}, + {"__hello_alias__", _Py_M____hello__, (int)sizeof(_Py_M____hello__), false, GET_CODE(__hello__)}, + {"__phello_alias__", _Py_M____hello__, (int)sizeof(_Py_M____hello__), true, GET_CODE(__hello__)}, + {"__phello_alias__.spam", _Py_M____hello__, (int)sizeof(_Py_M____hello__), false, GET_CODE(__hello__)}, + {"__phello__", _Py_M____phello__, (int)sizeof(_Py_M____phello__), true, GET_CODE(__phello__)}, + {"__phello__.__init__", _Py_M____phello__, (int)sizeof(_Py_M____phello__), false, GET_CODE(__phello__)}, + {"__phello__.ham", _Py_M____phello___ham, (int)sizeof(_Py_M____phello___ham), true, GET_CODE(__phello___ham)}, + {"__phello__.ham.__init__", _Py_M____phello___ham, (int)sizeof(_Py_M____phello___ham), false, GET_CODE(__phello___ham)}, + {"__phello__.ham.eggs", _Py_M____phello___ham_eggs, (int)sizeof(_Py_M____phello___ham_eggs), false, GET_CODE(__phello___ham_eggs)}, + {"__phello__.spam", _Py_M____phello___spam, (int)sizeof(_Py_M____phello___spam), false, GET_CODE(__phello___spam)}, + {"__hello_only__", _Py_M__frozen_only, (int)sizeof(_Py_M__frozen_only), false, GET_CODE(frozen_only)}, {0, 0, 0} /* test sentinel */ }; const struct _frozen *_PyImport_FrozenBootstrap = bootstrap_modules; diff --git a/Python/import.c b/Python/import.c index 1db5b9333bbba1..df577800528d27 100644 --- a/Python/import.c +++ b/Python/import.c @@ -2021,9 +2021,9 @@ find_frozen(PyObject *nameobj, struct frozen_info *info) } static PyObject * -unmarshal_frozen_code(struct frozen_info *info) +unmarshal_frozen_code(PyInterpreterState *interp, struct frozen_info *info) { - if (info->get_code) { + if (info->get_code && _Py_IsMainInterpreter(interp)) { PyObject *code = info->get_code(); assert(code != NULL); return code; @@ -2070,7 +2070,7 @@ PyImport_ImportFrozenModuleObject(PyObject *name) set_frozen_error(status, name); return -1; } - co = unmarshal_frozen_code(&info); + co = unmarshal_frozen_code(tstate->interp, &info); if (co == NULL) { return -1; } @@ -3528,7 +3528,8 @@ _imp_get_frozen_object_impl(PyObject *module, PyObject *name, return NULL; } - PyObject *codeobj = unmarshal_frozen_code(&info); + PyInterpreterState *interp = _PyInterpreterState_GET(); + PyObject *codeobj = unmarshal_frozen_code(interp, &info); if (dataobj != Py_None) { PyBuffer_Release(&buf); }