The default was "off". Switching it to "on" means users get the benefit of frozen stdlib modules without having to do anything. There's a special-case for running-in-source-tree, so contributors don't get surprised when their stdlib changes don't get used.
https://bugs.python.org/issue45020
* ``-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.
+ if this is an installed Python (the normal case). If it's under
+ development (running from the source tree) then the default is "off".
Note that the "importlib_bootstrap" and "importlib_bootstrap_external"
frozen modules are always used, even if this flag is set to "off".
extern int _Py_add_relfile(wchar_t *dirname,
const wchar_t *relfile,
size_t bufsize);
+extern size_t _Py_find_basename(const wchar_t *filename);
// Macros to protect CRT calls against instant termination when passed an
// invalid parameter (bpo-23524). IPH stands for Invalid Parameter Handler.
files, dirs = [], []
# Not all modules are required to have a __file__ attribute. See
# PEP 420 for more details.
- if hasattr(os, '__file__'):
+ here = getattr(sys, '_stdlib_dir', None)
+ if not here and hasattr(os, '__file__'):
here = os.path.dirname(os.__file__)
+ if here:
files.extend(["LICENSE.txt", "LICENSE"])
dirs.extend([os.path.join(here, os.pardir), here, os.curdir])
builtins.license = _sitebuiltins._Printer(
class EmbeddingTestsMixin:
def setUp(self):
exename = "_testembed"
+ builddir = os.path.dirname(sys.executable)
if MS_WINDOWS:
ext = ("_d" if debug_build(sys.executable) else "") + ".exe"
exename += ext
- exepath = os.path.dirname(sys.executable)
+ exepath = builddir
else:
- exepath = os.path.join(support.REPO_ROOT, "Programs")
+ exepath = os.path.join(builddir, 'Programs')
self.test_exe = exe = os.path.join(exepath, exename)
if not os.path.exists(exe):
self.skipTest("%r doesn't exist" % exe)
'pathconfig_warnings': 1,
'_init_main': 1,
'_isolated_interpreter': 0,
- 'use_frozen_modules': 0,
+ 'use_frozen_modules': 1,
}
if MS_WINDOWS:
CONFIG_COMPAT.update({
# The current getpath.c doesn't determine the stdlib dir
# in this case.
'stdlib_dir': '',
+ 'use_frozen_modules': -1,
}
self.default_program_name(config)
env = {'TESTPATH': os.path.pathsep.join(paths)}
# The current getpath.c doesn't determine the stdlib dir
# in this case.
'stdlib_dir': '',
+ 'use_frozen_modules': -1,
# overridden by PyConfig
'program_name': 'conf_program_name',
'base_executable': 'conf_executable',
'stdlib_dir': stdlib,
}
self.default_program_name(config)
+ if not config['executable']:
+ config['use_frozen_modules'] = -1
env = {'TESTHOME': home, 'PYTHONPATH': paths_str}
self.check_all_configs("test_init_setpythonhome", config,
api=API_COMPAT, env=env)
# The current getpath.c doesn't determine the stdlib dir
# in this case.
'stdlib_dir': None,
+ 'use_frozen_modules': -1,
}
env = self.copy_paths_by_env(config)
self.check_all_configs("test_init_compat_config", config,
config['base_prefix'] = pyvenv_home
config['prefix'] = pyvenv_home
config['stdlib_dir'] = os.path.join(pyvenv_home, 'lib')
+ config['use_frozen_modules'] = 1
ver = sys.version_info
dll = f'python{ver.major}'
# The current getpath.c doesn't determine the stdlib dir
# in this case.
config['stdlib_dir'] = None
+ config['use_frozen_modules'] = -1
env = self.copy_paths_by_env(config)
self.check_all_configs("test_init_compat_config", config,
Add a new command line option, "-X frozen_modules=[on|off]" to opt out of
(or into) using optional frozen modules. This defaults to "on" (or "off" if
-it's a debug build).
+it's running out of the source tree).
..
}
+size_t
+_Py_find_basename(const wchar_t *filename)
+{
+ for (size_t i = wcslen(filename); i > 0; --i) {
+ if (filename[i] == SEP) {
+ return i + 1;
+ }
+ }
+ return 0;
+}
+
+
/* Get the current directory. buflen is the buffer size in wide characters
including the null character. Decode the path from the locale encoding.
#ifdef MS_WINDOWS
config->legacy_windows_stdio = -1;
#endif
+ config->use_frozen_modules = -1;
}
}
+/* Determine if the current build is a "development" build (e.g. running
+ out of the source tree) or not.
+
+ A return value of -1 indicates that we do not know.
+ */
+static int
+is_dev_env(PyConfig *config)
+{
+ // This should only ever get called early in runtime initialization,
+ // before the global path config is written. Otherwise we would
+ // use Py_GetProgramFullPath() and _Py_GetStdlibDir().
+ assert(config != NULL);
+
+ const wchar_t *executable = config->executable;
+ const wchar_t *stdlib = config->stdlib_dir;
+ if (executable == NULL || *executable == L'\0' ||
+ stdlib == NULL || *stdlib == L'\0') {
+ // _PyPathConfig_Calculate() hasn't run yet.
+ return -1;
+ }
+ size_t len = _Py_find_basename(executable);
+ if (wcscmp(executable + len, L"python") != 0 &&
+ wcscmp(executable + len, L"python.exe") != 0) {
+ return 0;
+ }
+ /* If dirname() is the same for both then it is a dev build. */
+ if (len != _Py_find_basename(stdlib)) {
+ return 0;
+ }
+ // We do not bother normalizing the two filenames first since
+ // for config_init_import() is does the right thing as-is.
+ if (wcsncmp(stdlib, executable, len) != 0) {
+ return 0;
+ }
+ return 1;
+}
+
+
static PyStatus
config_init_import(PyConfig *config, int compute_path_config)
{
}
/* -X frozen_modules=[on|off] */
- const wchar_t *value = config_get_xoption_value(config, L"frozen_modules");
- if (value == NULL) {
- // For now we always default to "off".
- // In the near future we will be factoring in PGO and in-development.
- config->use_frozen_modules = 0;
- }
- else if (wcscmp(value, L"on") == 0) {
- config->use_frozen_modules = 1;
- }
- else if (wcscmp(value, L"off") == 0) {
- config->use_frozen_modules = 0;
- }
- else if (wcslen(value) == 0) {
- // "-X frozen_modules" and "-X frozen_modules=" both imply "on".
- config->use_frozen_modules = 1;
- }
- else {
- return PyStatus_Error("bad value for option -X frozen_modules "
- "(expected \"on\" or \"off\")");
+ if (config->use_frozen_modules < 0) {
+ const wchar_t *value = config_get_xoption_value(config, L"frozen_modules");
+ if (value == NULL) {
+ int isdev = is_dev_env(config);
+ if (isdev >= 0) {
+ config->use_frozen_modules = !isdev;
+ }
+ }
+ else if (wcscmp(value, L"on") == 0) {
+ config->use_frozen_modules = 1;
+ }
+ else if (wcscmp(value, L"off") == 0) {
+ config->use_frozen_modules = 0;
+ }
+ else if (wcslen(value) == 0) {
+ // "-X frozen_modules" and "-X frozen_modules=" both imply "on".
+ config->use_frozen_modules = 1;
+ }
+ else {
+ return PyStatus_Error("bad value for option -X frozen_modules "
+ "(expected \"on\" or \"off\")");
+ }
}
return _PyStatus_OK();