Skip to content
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

bpo-45696: Deep-freeze #29118

Merged
merged 46 commits into from
Nov 11, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
a04d702
Add placeholders for stdlib modules.
ericsnowcurrently Aug 12, 2021
033acab
Add the frozen stdlib modules.
ericsnowcurrently Jun 8, 2021
0591cbd
Freeze the stdlib modules loaded during startup.
ericsnowcurrently Jun 8, 2021
72666d4
Group the import-related configs.
ericsnowcurrently Aug 31, 2021
ce71894
Add the -X frozen_modules CLI option.
ericsnowcurrently Aug 31, 2021
ab500e2
Ignore frozen modules depending on "-X frozen_modules".
ericsnowcurrently Aug 31, 2021
932e563
Remember the stdlib dir during startup.
ericsnowcurrently Jun 12, 2021
2bb2a43
First part of deepfreeze
gvanrossum Aug 31, 2021
cb6c74d
Add _Py_GetMainConfig().
ericsnowcurrently Aug 31, 2021
e94240e
Let _Py_GetStdlibDir() fall back to a config.
ericsnowcurrently Aug 31, 2021
e106e41
Add _PyConfig_InitImportConfig().
ericsnowcurrently Aug 31, 2021
80506bb
Identify whether or not the executed is running installed.
ericsnowcurrently Aug 31, 2021
9ac5b11
Default to "off" if in development.
ericsnowcurrently Aug 31, 2021
acfc2a2
Remember if built with --enable-optimizations.
ericsnowcurrently Aug 31, 2021
ef653fb
Default to "on" for PGO builds.
ericsnowcurrently Aug 31, 2021
f70319b
Make deepfreeze actually work
gvanrossum Aug 31, 2021
a381ccf
Add a NEWS entry.
ericsnowcurrently Aug 31, 2021
47e3917
Generate all deepfreeze items in Makefile.pre.in
gvanrossum Sep 1, 2021
20992e9
Don't use GET_CODE in the extern declarations
gvanrossum Sep 1, 2021
692959b
Merge branch 'pr-28107' into deepfreeze
gvanrossum Sep 1, 2021
77812d6
Exclude encodings module for now
gvanrossum Sep 1, 2021
80cf00f
Regenerate Python/frozen.c
gvanrossum Sep 5, 2021
1d29e95
Make [deep]freeze process mess noisy
gvanrossum Sep 5, 2021
9818c44
Regenerated MSVC project files
gvanrossum Sep 6, 2021
9e817b2
Merge branch 'main' into deepfreeze
gvanrossum Oct 20, 2021
2d89d0a
Make deep-freezing work again
gvanrossum Oct 20, 2021
8d029ae
Make deep-freezing *really* work
gvanrossum Oct 21, 2021
27518ff
Merge branch 'main' into deepfreeze
gvanrossum Oct 21, 2021
5e98069
Fix test_ctypes and struct_frozen example in docs
gvanrossum Oct 21, 2021
239ee0b
Merge branch 'main' into deepfreeze
gvanrossum Nov 1, 2021
34b6da4
Undo some badly merged files
gvanrossum Nov 1, 2021
2c54cf0
Add dependency on bootstrap for _bootstrap_python
gvanrossum Nov 1, 2021
502b566
Remove Programs/_bootstrap_python in 'make clean'
gvanrossum Nov 2, 2021
6733acd
Move _bootstrap_python to toplevel dir, to get build working
gvanrossum Nov 2, 2021
70d10f8
Regenerate Makefile.pre.in
gvanrossum Nov 2, 2021
11182f8
Use 'make' shortcuts in deep-freeze rules
gvanrossum Nov 2, 2021
b4103cb
Fix test_import, test_regrtest, test_trace
gvanrossum Nov 2, 2021
b7abc3e
Fix out-of-tree builds
gvanrossum Nov 2, 2021
6e11529
📜🤖 Added by blurb_it.
blurb-it[bot] Nov 3, 2021
fa52af4
Open files in UTF-8 mode
gvanrossum Nov 4, 2021
e4f31d0
Solve out-of-tree build differently
gvanrossum Nov 9, 2021
76fb82d
Use _bootstrap_python to create pybuilddir.txt and sysconfig
gvanrossum Nov 9, 2021
cb69845
Remove Python/deepfreeze contents
gvanrossum Nov 9, 2021
0c9de73
Merge remote-tracking branch 'origin/main' into deepfreeze
gvanrossum Nov 9, 2021
c747a0f
Don't use $@ and $<, they don't work on FreeBSD
gvanrossum Nov 10, 2021
f40d138
Fix out of tree builds by adding $(srcdir)
gvanrossum Nov 10, 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
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ Modules/clinic/*.h linguist-generated=true
Objects/clinic/*.h linguist-generated=true
PC/clinic/*.h linguist-generated=true
Python/clinic/*.h linguist-generated=true
Python/deepfreeze/*.c linguist-generated=true
Python/frozen_modules/*.h linguist-generated=true
Python/frozen_modules/MANIFEST linguist-generated=true
Include/internal/pycore_ast.h linguist-generated=true
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ Lib/distutils/command/*.pdb
Lib/lib2to3/*.pickle
Lib/test/data/*
!Lib/test/data/README
/_bootstrap_python
/Makefile
/Makefile.pre
Mac/Makefile
Expand Down Expand Up @@ -115,6 +116,7 @@ Tools/unicode/data/
/platform
/profile-clean-stamp
/profile-run-stamp
/Python/deepfreeze/*.c
/pybuilddir.txt
/pyconfig.h
/python-config
Expand Down
4 changes: 3 additions & 1 deletion Doc/library/ctypes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1087,7 +1087,9 @@ size, we show only how this table can be read with :mod:`ctypes`::
>>> class struct_frozen(Structure):
... _fields_ = [("name", c_char_p),
... ("code", POINTER(c_ubyte)),
... ("size", c_int)]
... ("size", c_int),
... ("get_code", POINTER(c_ubyte)), # Function pointer
... ]
...
>>>

Expand Down
1 change: 1 addition & 0 deletions Include/cpython/import.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ struct _frozen {
const char *name; /* ASCII encoded string */
const unsigned char *code;
int size;
PyObject *(*get_code)(void);
};

/* Embedding apps may change this pointer to point to their favorite
Expand Down
4 changes: 3 additions & 1 deletion Lib/ctypes/test/test_values.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,9 @@ def test_frozentable(self):
class struct_frozen(Structure):
_fields_ = [("name", c_char_p),
("code", POINTER(c_ubyte)),
("size", c_int)]
("size", c_int),
("get_code", POINTER(c_ubyte)), # Function ptr
]
FrozenTable = POINTER(struct_frozen)

modules = []
Expand Down
185 changes: 182 additions & 3 deletions Makefile.pre.in
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,7 @@ LIBOBJS= @LIBOBJS@

PYTHON= python$(EXE)
BUILDPYTHON= python$(BUILDEXE)
BOOTSTRAP= _bootstrap_python

PYTHON_FOR_REGEN?=@PYTHON_FOR_REGEN@
UPDATE_FILE=$(PYTHON_FOR_REGEN) $(srcdir)/Tools/scripts/update_file.py
Expand Down Expand Up @@ -448,6 +449,30 @@ OBJECT_OBJS= \
Objects/unionobject.o \
Objects/weakrefobject.o

# DEEPFREEZE_OBJS is auto-generated by Tools/scripts/freeze_modules.py.
DEEPFREEZE_OBJS = \
Python/deepfreeze/importlib._bootstrap.o \
Python/deepfreeze/importlib._bootstrap_external.o \
Python/deepfreeze/zipimport.o \
Python/deepfreeze/abc.o \
Python/deepfreeze/codecs.o \
Python/deepfreeze/io.o \
Python/deepfreeze/_collections_abc.o \
Python/deepfreeze/_sitebuiltins.o \
Python/deepfreeze/genericpath.o \
Python/deepfreeze/ntpath.o \
Python/deepfreeze/posixpath.o \
Python/deepfreeze/os.o \
Python/deepfreeze/site.o \
Python/deepfreeze/stat.o \
Python/deepfreeze/__hello__.o \
Python/deepfreeze/__phello__.o \
Python/deepfreeze/__phello__.ham.o \
Python/deepfreeze/__phello__.ham.eggs.o \
Python/deepfreeze/__phello__.spam.o \
Python/deepfreeze/frozen_only.o
# End DEEPFREEZE_OBJS

##########################################################################
# objects that get linked into the Python library
LIBRARY_OBJS_OMIT_FROZEN= \
Expand All @@ -460,6 +485,7 @@ LIBRARY_OBJS_OMIT_FROZEN= \

LIBRARY_OBJS= \
$(LIBRARY_OBJS_OMIT_FROZEN) \
$(DEEPFREEZE_OBJS) \
Python/frozen.o

##########################################################################
Expand Down Expand Up @@ -602,9 +628,9 @@ platform: $(BUILDPYTHON) pybuilddir.txt
# problems by creating a dummy pybuilddir.txt just to allow interpreter
# initialization to succeed. It will be overwritten by generate-posix-vars
# or removed in case of failure.
pybuilddir.txt: $(BUILDPYTHON)
pybuilddir.txt: $(BOOTSTRAP)
@echo "none" > ./pybuilddir.txt
$(RUNSHARED) $(PYTHON_FOR_BUILD) -S -m sysconfig --generate-posix-vars ;\
./$(BOOTSTRAP) -S -m sysconfig --generate-posix-vars ;\
if test $$? -ne 0 ; then \
echo "generate-posix-vars failed" ; \
rm -f ./pybuilddir.txt ; \
Expand Down Expand Up @@ -738,6 +764,158 @@ regen-test-frozenmain: $(BUILDPYTHON)
Programs/_testembed: Programs/_testembed.o $(LIBRARY_DEPS)
$(LINKCC) $(PY_CORE_LDFLAGS) $(LINKFORSHARED) -o $@ Programs/_testembed.o $(BLDLIBRARY) $(LIBS) $(MODLIBS) $(SYSLIBS)

############################################################################
# "Bootstrap Python" used to run deepfreeze.py

BOOTSTRAP_HEADERS = \
Python/frozen_modules/importlib._bootstrap.h \
Python/frozen_modules/importlib._bootstrap_external.h \
Python/frozen_modules/zipimport.h

Python/bootstrap_frozen.o: Python/bootstrap_frozen.c Include/cpython/import.h $(BOOTSTRAP_HEADERS)

$(BOOTSTRAP): $(LIBRARY_OBJS_OMIT_FROZEN) \
Python/bootstrap_frozen.o Programs/python.o
$(LINKCC) $(PY_CORE_LDFLAGS) -o $@ $(LIBRARY_OBJS_OMIT_FROZEN) \
Python/bootstrap_frozen.o \
Programs/python.o \
$(LIBS) $(MODLIBS) $(SYSLIBS)

############################################################################
# Deepfreeze targets

.PHONY: regen-deepfreeze
regen-deepfreeze: $(DEEPFREEZE_OBJS)

DEEPFREEZE_DEPS = \
$(BOOTSTRAP) \
pybuilddir.txt \
$(srcdir)/Tools/scripts/deepfreeze.py

# BEGIN: deepfreeze modules

Python/deepfreeze/importlib._bootstrap.c: $(srcdir)/Lib/importlib/_bootstrap.py $(DEEPFREEZE_DEPS)
@echo "Deepfreezing Python/deepfreeze/importlib._bootstrap.c from Lib/importlib/_bootstrap.py"
@./$(BOOTSTRAP) \
$(srcdir)/Tools/scripts/deepfreeze.py \
$(srcdir)/Lib/importlib/_bootstrap.py -m importlib._bootstrap -o Python/deepfreeze/importlib._bootstrap.c

Python/deepfreeze/importlib._bootstrap_external.c: $(srcdir)/Lib/importlib/_bootstrap_external.py $(DEEPFREEZE_DEPS)
@echo "Deepfreezing Python/deepfreeze/importlib._bootstrap_external.c from Lib/importlib/_bootstrap_external.py"
@./$(BOOTSTRAP) \
$(srcdir)/Tools/scripts/deepfreeze.py \
$(srcdir)/Lib/importlib/_bootstrap_external.py -m importlib._bootstrap_external -o Python/deepfreeze/importlib._bootstrap_external.c

Python/deepfreeze/zipimport.c: $(srcdir)/Lib/zipimport.py $(DEEPFREEZE_DEPS)
@echo "Deepfreezing Python/deepfreeze/zipimport.c from Lib/zipimport.py"
@./$(BOOTSTRAP) \
$(srcdir)/Tools/scripts/deepfreeze.py \
$(srcdir)/Lib/zipimport.py -m zipimport -o Python/deepfreeze/zipimport.c

Python/deepfreeze/abc.c: $(srcdir)/Lib/abc.py $(DEEPFREEZE_DEPS)
@echo "Deepfreezing Python/deepfreeze/abc.c from Lib/abc.py"
@./$(BOOTSTRAP) \
$(srcdir)/Tools/scripts/deepfreeze.py \
$(srcdir)/Lib/abc.py -m abc -o Python/deepfreeze/abc.c

Python/deepfreeze/codecs.c: $(srcdir)/Lib/codecs.py $(DEEPFREEZE_DEPS)
@echo "Deepfreezing Python/deepfreeze/codecs.c from Lib/codecs.py"
@./$(BOOTSTRAP) \
$(srcdir)/Tools/scripts/deepfreeze.py \
$(srcdir)/Lib/codecs.py -m codecs -o Python/deepfreeze/codecs.c

Python/deepfreeze/io.c: $(srcdir)/Lib/io.py $(DEEPFREEZE_DEPS)
@echo "Deepfreezing Python/deepfreeze/io.c from Lib/io.py"
@./$(BOOTSTRAP) \
$(srcdir)/Tools/scripts/deepfreeze.py \
$(srcdir)/Lib/io.py -m io -o Python/deepfreeze/io.c

Python/deepfreeze/_collections_abc.c: $(srcdir)/Lib/_collections_abc.py $(DEEPFREEZE_DEPS)
@echo "Deepfreezing Python/deepfreeze/_collections_abc.c from Lib/_collections_abc.py"
@./$(BOOTSTRAP) \
$(srcdir)/Tools/scripts/deepfreeze.py \
$(srcdir)/Lib/_collections_abc.py -m _collections_abc -o Python/deepfreeze/_collections_abc.c

Python/deepfreeze/_sitebuiltins.c: $(srcdir)/Lib/_sitebuiltins.py $(DEEPFREEZE_DEPS)
@echo "Deepfreezing Python/deepfreeze/_sitebuiltins.c from Lib/_sitebuiltins.py"
@./$(BOOTSTRAP) \
$(srcdir)/Tools/scripts/deepfreeze.py \
$(srcdir)/Lib/_sitebuiltins.py -m _sitebuiltins -o Python/deepfreeze/_sitebuiltins.c

Python/deepfreeze/genericpath.c: $(srcdir)/Lib/genericpath.py $(DEEPFREEZE_DEPS)
@echo "Deepfreezing Python/deepfreeze/genericpath.c from Lib/genericpath.py"
@./$(BOOTSTRAP) \
$(srcdir)/Tools/scripts/deepfreeze.py \
$(srcdir)/Lib/genericpath.py -m genericpath -o Python/deepfreeze/genericpath.c

Python/deepfreeze/ntpath.c: $(srcdir)/Lib/ntpath.py $(DEEPFREEZE_DEPS)
@echo "Deepfreezing Python/deepfreeze/ntpath.c from Lib/ntpath.py"
@./$(BOOTSTRAP) \
$(srcdir)/Tools/scripts/deepfreeze.py \
$(srcdir)/Lib/ntpath.py -m ntpath -o Python/deepfreeze/ntpath.c

Python/deepfreeze/posixpath.c: $(srcdir)/Lib/posixpath.py $(DEEPFREEZE_DEPS)
@echo "Deepfreezing Python/deepfreeze/posixpath.c from Lib/posixpath.py"
@./$(BOOTSTRAP) \
$(srcdir)/Tools/scripts/deepfreeze.py \
$(srcdir)/Lib/posixpath.py -m posixpath -o Python/deepfreeze/posixpath.c

Python/deepfreeze/os.c: $(srcdir)/Lib/os.py $(DEEPFREEZE_DEPS)
@echo "Deepfreezing Python/deepfreeze/os.c from Lib/os.py"
@./$(BOOTSTRAP) \
$(srcdir)/Tools/scripts/deepfreeze.py \
$(srcdir)/Lib/os.py -m os -o Python/deepfreeze/os.c

Python/deepfreeze/site.c: $(srcdir)/Lib/site.py $(DEEPFREEZE_DEPS)
@echo "Deepfreezing Python/deepfreeze/site.c from Lib/site.py"
@./$(BOOTSTRAP) \
$(srcdir)/Tools/scripts/deepfreeze.py \
$(srcdir)/Lib/site.py -m site -o Python/deepfreeze/site.c

Python/deepfreeze/stat.c: $(srcdir)/Lib/stat.py $(DEEPFREEZE_DEPS)
@echo "Deepfreezing Python/deepfreeze/stat.c from Lib/stat.py"
@./$(BOOTSTRAP) \
$(srcdir)/Tools/scripts/deepfreeze.py \
$(srcdir)/Lib/stat.py -m stat -o Python/deepfreeze/stat.c

Python/deepfreeze/__hello__.c: $(srcdir)/Lib/__hello__.py $(DEEPFREEZE_DEPS)
@echo "Deepfreezing Python/deepfreeze/__hello__.c from Lib/__hello__.py"
@./$(BOOTSTRAP) \
$(srcdir)/Tools/scripts/deepfreeze.py \
$(srcdir)/Lib/__hello__.py -m __hello__ -o Python/deepfreeze/__hello__.c

Python/deepfreeze/__phello__.c: $(srcdir)/Lib/__phello__/__init__.py $(DEEPFREEZE_DEPS)
@echo "Deepfreezing Python/deepfreeze/__phello__.c from Lib/__phello__/__init__.py"
@./$(BOOTSTRAP) \
$(srcdir)/Tools/scripts/deepfreeze.py \
$(srcdir)/Lib/__phello__/__init__.py -m __phello__ -o Python/deepfreeze/__phello__.c

Python/deepfreeze/__phello__.ham.c: $(srcdir)/Lib/__phello__/ham/__init__.py $(DEEPFREEZE_DEPS)
@echo "Deepfreezing Python/deepfreeze/__phello__.ham.c from Lib/__phello__/ham/__init__.py"
@./$(BOOTSTRAP) \
$(srcdir)/Tools/scripts/deepfreeze.py \
$(srcdir)/Lib/__phello__/ham/__init__.py -m __phello__.ham -o Python/deepfreeze/__phello__.ham.c

Python/deepfreeze/__phello__.ham.eggs.c: $(srcdir)/Lib/__phello__/ham/eggs.py $(DEEPFREEZE_DEPS)
@echo "Deepfreezing Python/deepfreeze/__phello__.ham.eggs.c from Lib/__phello__/ham/eggs.py"
@./$(BOOTSTRAP) \
$(srcdir)/Tools/scripts/deepfreeze.py \
$(srcdir)/Lib/__phello__/ham/eggs.py -m __phello__.ham.eggs -o Python/deepfreeze/__phello__.ham.eggs.c

Python/deepfreeze/__phello__.spam.c: $(srcdir)/Lib/__phello__/spam.py $(DEEPFREEZE_DEPS)
@echo "Deepfreezing Python/deepfreeze/__phello__.spam.c from Lib/__phello__/spam.py"
@./$(BOOTSTRAP) \
$(srcdir)/Tools/scripts/deepfreeze.py \
$(srcdir)/Lib/__phello__/spam.py -m __phello__.spam -o Python/deepfreeze/__phello__.spam.c

Python/deepfreeze/frozen_only.c: $(srcdir)/Tools/freeze/flag.py $(DEEPFREEZE_DEPS)
@echo "Deepfreezing Python/deepfreeze/frozen_only.c from Tools/freeze/flag.py"
@./$(BOOTSTRAP) \
$(srcdir)/Tools/scripts/deepfreeze.py \
$(srcdir)/Tools/freeze/flag.py -m frozen_only -o Python/deepfreeze/frozen_only.c

# END: deepfreeze modules

############################################################################
# frozen modules (including importlib)

Expand Down Expand Up @@ -2017,7 +2195,8 @@ clean-retain-profile: pycremoval
find build -name '*.py[co]' -exec rm -f {} ';' || true
-rm -f pybuilddir.txt
-rm -f Lib/lib2to3/*Grammar*.pickle
-rm -f Programs/_testembed Programs/_freeze_module
-rm -f Programs/_testembed Programs/_freeze_module $(BOOTSTRAP)
-rm -f Python/deepfreeze/*.[co]
-find build -type f -a ! -name '*.gc??' -exec rm -f {} ';'
-rm -f Include/pydtrace_probes.h
-rm -f profile-gen-stamp
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Skip the marshal step for frozen modules by generating C code that produces a set of ready-to-use code objects. This speeds up startup time by another 10% or more.
45 changes: 45 additions & 0 deletions Python/bootstrap_frozen.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@

/* Frozen modules bootstrap */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file is essentially identical to Python/frozen.c if we cut out the non-bootstrap parts, right? So could we just use frozen.c with something like #ifndef FROZEN_BOOTRAP around the non-bootstrap parts?

Otherwise I recommend generating the parts of this file with Tools/scripts/freeze_modules.py, the same as with frozen.c. If we don't then this will break when someone adds another module to the BOOTSTRAP list in freeze_modules.py.


/* This file is linked with "bootstrap Python"
which is used (only) to run Tools/scripts/deepfreeze.py. */

#include "Python.h"
#include "pycore_import.h"

/* Includes for frozen modules: */
#include "frozen_modules/importlib._bootstrap.h"
#include "frozen_modules/importlib._bootstrap_external.h"
#include "frozen_modules/zipimport.h"
/* End includes */

/* Note that a negative size indicates a package. */

static const struct _frozen bootstrap_modules[] = {
{"_frozen_importlib", _Py_M__importlib__bootstrap, (int)sizeof(_Py_M__importlib__bootstrap)},
{"_frozen_importlib_external", _Py_M__importlib__bootstrap_external, (int)sizeof(_Py_M__importlib__bootstrap_external)},
{"zipimport", _Py_M__zipimport, (int)sizeof(_Py_M__zipimport)},
{0, 0, 0} /* bootstrap sentinel */
};
static const struct _frozen stdlib_modules[] = {
{0, 0, 0} /* stdlib sentinel */
};
static const struct _frozen test_modules[] = {
{0, 0, 0} /* test sentinel */
};
const struct _frozen *_PyImport_FrozenBootstrap = bootstrap_modules;
const struct _frozen *_PyImport_FrozenStdlib = stdlib_modules;
const struct _frozen *_PyImport_FrozenTest = test_modules;

static const struct _module_alias aliases[] = {
{"_frozen_importlib", "importlib._bootstrap"},
{"_frozen_importlib_external", "importlib._bootstrap_external"},
{0, 0} /* aliases sentinel */
};
const struct _module_alias *_PyImport_FrozenAliases = aliases;


/* Embedding apps may change this pointer to point to their favorite
collection of frozen modules: */

const struct _frozen *PyImport_FrozenModules = NULL;
6 changes: 6 additions & 0 deletions Python/deepfreeze/README.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
This directory contains the generated .c files for all the deep-frozen
modules. Python/frozen.c depends on these files.

None of these files are committed into the repo.

See Tools/scripts/freeze_modules.py for more info.
Loading