Skip to content

bpo-45020: Add -X frozen_modules=[on|off] to explicitly control use of frozen modules. #28320

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
ac7ff4f
Add the -X frozen_modules CLI option and PyConfig.use_frozen_modules.
ericsnowcurrently Aug 31, 2021
7d2a470
Ignore frozen modules depending on "-X frozen_modules".
ericsnowcurrently Aug 31, 2021
aacaf24
Explicitly control PyConfig.use_frozen_modules during tests.
ericsnowcurrently Sep 1, 2021
5a6e40c
Factor out is_essential_frozen_module().
ericsnowcurrently Sep 13, 2021
4da6302
Treat __main__ as an essential frozen module.
ericsnowcurrently Sep 7, 2021
26469e5
Look up $_PYTHONTESTFROZENMODULES using getenv() instead of in os.env…
ericsnowcurrently Sep 7, 2021
f274b1c
Add zipimport to the list of essential frozen modules.
ericsnowcurrently Sep 7, 2021
6881cb8
Flip around the arg to the "frozen_modules" test helper.
ericsnowcurrently Sep 8, 2021
785addf
Disable frozen modules if $_PYTHONTESTFROZENMODULES is 0.
ericsnowcurrently Sep 8, 2021
a8cb984
Add the "usefrozen" arg to CleanImport.__init__().
ericsnowcurrently Sep 8, 2021
6d9d469
Ignore decode errors in find_frozen().
ericsnowcurrently Sep 8, 2021
df19f79
Fix test_ctypes.
ericsnowcurrently Sep 8, 2021
abfef0b
Ensure we get the frozen module.
ericsnowcurrently Sep 13, 2021
e0258cf
Fix docstrings for the import test helpers.
ericsnowcurrently Sep 13, 2021
0fd66d4
Add a NEWS entry.
ericsnowcurrently Aug 31, 2021
30e8cd1
Remember the stdlib dir during startup (adding PyConfig.stdlib_dir).
ericsnowcurrently Jun 12, 2021
1a83ef6
Add _Py_GetMainConfig().
ericsnowcurrently Aug 31, 2021
3b57bca
Let _Py_GetStdlibDir() fall back to a config.
ericsnowcurrently Aug 31, 2021
b45e5f7
Add _PyConfig_InitImportConfig().
ericsnowcurrently Aug 31, 2021
2298dc0
Identify whether or not the executable is running installed (adding _…
ericsnowcurrently Aug 31, 2021
2b67c44
Default to "off" if in development.
ericsnowcurrently Aug 31, 2021
0933e4d
Add a NEWS entry.
ericsnowcurrently Aug 31, 2021
4823ad6
Fix test_embed for PyConfig.stdlib_dir.
ericsnowcurrently Sep 7, 2021
0e5c617
In _Py_GetStdlibDir(), treat "" as though it were NULL.
ericsnowcurrently Sep 7, 2021
d427bae
Fix test_embed for "Default to "off" if in development.".
ericsnowcurrently Sep 7, 2021
f6bd2fd
Fix test_cmd_line_script for "Default to "off" if in development.".
ericsnowcurrently Sep 7, 2021
965c7fe
_Py_IsInstalled -> _Py_IsDevelopmentEnv.
ericsnowcurrently Sep 7, 2021
4f49adc
Stop using the frozen helper in test_cmd_line_script.
ericsnowcurrently Sep 7, 2021
5297edc
Allow for a .exe suffix on the executable.
ericsnowcurrently Sep 9, 2021
2b6f978
Drop _Py_IsDevelopmentEnv().
ericsnowcurrently Sep 10, 2021
dc2aa34
Drop -_Py_GetStdlibDir() and PyConfig.stdlib_dir.
ericsnowcurrently Sep 10, 2021
218ba84
Default to "-X frozen_modules=off" if built with --with-debug.
ericsnowcurrently Sep 10, 2021
81b66c4
Fix test_embed.
ericsnowcurrently Sep 10, 2021
8a0e2d6
Fix the ASAN job.
ericsnowcurrently Sep 11, 2021
015035d
Fix the docs.
ericsnowcurrently Sep 13, 2021
7e2ce41
Drop unused changes.
ericsnowcurrently Sep 13, 2021
b73486a
Fix the Windows CI jobs.
ericsnowcurrently Sep 13, 2021
fc2123b
Fix test_embed on non-debug builds.
ericsnowcurrently Sep 13, 2021
c3ed104
Fix test_embed on non-debug builds (on Windows).
ericsnowcurrently Sep 14, 2021
9b110fd
Drop parse_env_var_flag().
ericsnowcurrently Sep 14, 2021
9df64c1
Don't use env var _PYTHONTESTFROZENMODULES when running _testembed.
ericsnowcurrently Sep 14, 2021
ccd468e
Do not use an env var to override PyConfig.use_frozen_modules.
ericsnowcurrently Sep 14, 2021
5bc6730
For now, always default to "-X frozen_modules=off".
ericsnowcurrently Sep 14, 2021
f50476c
Change PyConfig.use_frozen_modules from bool to int.
ericsnowcurrently Sep 14, 2021
0dcdb44
Fix use of _PyConfig_InitImportConfig().
ericsnowcurrently Sep 14, 2021
2ec9909
Drop a superfluous comment.
ericsnowcurrently Sep 14, 2021
80d8fcd
Fix a typo.
ericsnowcurrently Sep 14, 2021
aff01e6
Simplify a comment.
ericsnowcurrently Sep 14, 2021
7476d65
Simplify use_frozen().
ericsnowcurrently Sep 14, 2021
e3e5744
Fix a comment.
ericsnowcurrently Sep 14, 2021
669beac
Collapse some lines.
ericsnowcurrently Sep 14, 2021
c644012
Drop _Py_GetMainConfig().
ericsnowcurrently Sep 14, 2021
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
9 changes: 9 additions & 0 deletions Doc/using/cmdline.rst
Original file line number Diff line number Diff line change
Expand Up @@ -480,6 +480,12 @@ Miscellaneous options
objects and pyc files are desired as well as supressing the extra visual
location indicators when the interpreter displays tracebacks. See also
:envvar:`PYTHONNODEBUGRANGES`.
* ``-X frozen_modules`` determines whether or not frozen modules are
ignored by the import machinery. A value of "on" means they get
imported and "off" means they are ignored. The default is "on"
for non-debug builds (the normal case) and "off" for debug builds.
Note that the "importlib_bootstrap" and "importlib_bootstrap_external"
frozen modules are always used, even if this flag is set to "off".

It also allows passing arbitrary values and retrieving them through the
:data:`sys._xoptions` dictionary.
Expand Down Expand Up @@ -518,6 +524,9 @@ Miscellaneous options
.. versionadded:: 3.11
The ``-X no_debug_ranges`` option.

.. versionadded:: 3.11
The ``-X frozen_modules`` option.


Options you shouldn't use
~~~~~~~~~~~~~~~~~~~~~~~~~
Expand Down
1 change: 1 addition & 0 deletions Include/cpython/initconfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ typedef struct PyConfig {
int legacy_windows_stdio;
#endif
wchar_t *check_hash_pycs_mode;
int use_frozen_modules;

/* --- Path configuration inputs ------------ */
int pathconfig_warnings;
Expand Down
1 change: 1 addition & 0 deletions Include/internal/pycore_initconfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ extern PyStatus _PyConfig_Copy(
extern PyStatus _PyConfig_InitPathConfig(
PyConfig *config,
int compute_path_config);
extern PyStatus _PyConfig_InitImportConfig(PyConfig *config);
extern PyStatus _PyConfig_Read(PyConfig *config, int compute_path_config);
extern PyStatus _PyConfig_Write(const PyConfig *config,
struct pyruntimestate *runtime);
Expand Down
3 changes: 3 additions & 0 deletions Include/internal/pycore_interp.h
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,9 @@ struct _is {
PyObject *builtins;
// importlib module
PyObject *importlib;
// override for config->use_frozen_modules (for tests)
// (-1: "off", 1: "on", 0: no override)
int override_frozen_modules;

/* Used in Modules/_threadmodule.c. */
long num_threads;
Expand Down
6 changes: 4 additions & 2 deletions Lib/ctypes/test/test_values.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,14 +72,16 @@ class struct_frozen(Structure):
self.assertGreater(abs(entry.size), 10)
self.assertTrue([entry.code[i] for i in range(abs(entry.size))])
# Check the module's package-ness.
spec = importlib.util.find_spec(modname)
with import_helper.frozen_modules():
spec = importlib.util.find_spec(modname)
if entry.size < 0:
# It's a package.
self.assertIsNotNone(spec.submodule_search_locations)
else:
self.assertIsNone(spec.submodule_search_locations)

expected = imp._frozen_module_names()
with import_helper.frozen_modules():
expected = imp._frozen_module_names()
self.maxDiff = None
self.assertEqual(modules, expected, "PyImport_FrozenModules example "
"in Doc/library/ctypes.rst may be out of date")
Expand Down
34 changes: 31 additions & 3 deletions Lib/test/support/import_helper.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import contextlib
import _imp
import importlib
import importlib.util
import os
Expand Down Expand Up @@ -109,7 +110,24 @@ def _save_and_block_module(name, orig_modules):
return saved


def import_fresh_module(name, fresh=(), blocked=(), deprecated=False):
@contextlib.contextmanager
def frozen_modules(enabled=True):
"""Force frozen modules to be used (or not).

This only applies to modules that haven't been imported yet.
Also, some essential modules will always be imported frozen.
"""
_imp._override_frozen_modules_for_tests(1 if enabled else -1)
try:
yield
finally:
_imp._override_frozen_modules_for_tests(0)


def import_fresh_module(name, fresh=(), blocked=(), *,
deprecated=False,
usefrozen=False,
):
"""Import and return a module, deliberately bypassing sys.modules.

This function imports and returns a fresh copy of the named Python module
Expand All @@ -133,6 +151,9 @@ def import_fresh_module(name, fresh=(), blocked=(), deprecated=False):

This function will raise ImportError if the named module cannot be
imported.

If "usefrozen" is False (the default) then the frozen importer is
disabled (except for essential modules like importlib._bootstrap).
"""
# NOTE: test_heapq, test_json and test_warnings include extra sanity checks
# to make sure that this utility function is working as expected
Expand All @@ -148,7 +169,8 @@ def import_fresh_module(name, fresh=(), blocked=(), deprecated=False):
for blocked_name in blocked:
if not _save_and_block_module(blocked_name, orig_modules):
names_to_remove.append(blocked_name)
fresh_module = importlib.import_module(name)
with frozen_modules(usefrozen):
fresh_module = importlib.import_module(name)
except ImportError:
fresh_module = None
finally:
Expand All @@ -169,9 +191,12 @@ class CleanImport(object):

with CleanImport("foo"):
importlib.import_module("foo") # new reference

If "usefrozen" is False (the default) then the frozen importer is
disabled (except for essential modules like importlib._bootstrap).
"""

def __init__(self, *module_names):
def __init__(self, *module_names, usefrozen=False):
self.original_modules = sys.modules.copy()
for module_name in module_names:
if module_name in sys.modules:
Expand All @@ -183,12 +208,15 @@ def __init__(self, *module_names):
if module.__name__ != module_name:
del sys.modules[module.__name__]
del sys.modules[module_name]
self._frozen_modules = frozen_modules(usefrozen)

def __enter__(self):
self._frozen_modules.__enter__()
return self

def __exit__(self, *ignore_exc):
sys.modules.update(self.original_modules)
self._frozen_modules.__exit__(*ignore_exc)


class DirsOnSysPath(object):
Expand Down
4 changes: 4 additions & 0 deletions Lib/test/support/os_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -599,6 +599,10 @@ def set(self, envvar, value):
def unset(self, envvar):
del self[envvar]

def copy(self):
# We do what os.environ.copy() does.
return dict(self)

def __enter__(self):
return self

Expand Down
2 changes: 2 additions & 0 deletions Lib/test/test_embed.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import shutil
import subprocess
import sys
import sysconfig
import tempfile
import textwrap

Expand Down Expand Up @@ -426,6 +427,7 @@ class InitConfigTests(EmbeddingTestsMixin, unittest.TestCase):
'pathconfig_warnings': 1,
'_init_main': 1,
'_isolated_interpreter': 0,
'use_frozen_modules': False,
}
if MS_WINDOWS:
CONFIG_COMPAT.update({
Expand Down
7 changes: 4 additions & 3 deletions Lib/test/test_frozen.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,17 @@

import sys
import unittest
from test.support import captured_stdout
from test.support import captured_stdout, import_helper


class TestFrozen(unittest.TestCase):
def test_frozen(self):
name = '__hello__'
if name in sys.modules:
del sys.modules[name]
with captured_stdout() as out:
import __hello__
with import_helper.frozen_modules():
with captured_stdout() as out:
import __hello__
self.assertEqual(out.getvalue(), 'Hello world!\n')


Expand Down
8 changes: 6 additions & 2 deletions Lib/test/test_importlib/frozen/test_finder.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,17 @@
import unittest
import warnings

from test.support import import_helper


class FindSpecTests(abc.FinderTests):

"""Test finding frozen modules."""

def find(self, name, path=None):
finder = self.machinery.FrozenImporter
return finder.find_spec(name, path)
with import_helper.frozen_modules():
return finder.find_spec(name, path)

def test_module(self):
name = '__hello__'
Expand Down Expand Up @@ -52,7 +55,8 @@ def find(self, name, path=None):
finder = self.machinery.FrozenImporter
with warnings.catch_warnings():
warnings.simplefilter("ignore", DeprecationWarning)
return finder.find_module(name, path)
with import_helper.frozen_modules():
return finder.find_module(name, path)

def test_module(self):
name = '__hello__'
Expand Down
Loading