aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/Tools/scripts/deepfreeze.py
diff options
context:
space:
mode:
authorGuido van Rossum <guido@python.org>2021-11-22 10:09:48 -0800
committerGitHub <noreply@github.com>2021-11-22 10:09:48 -0800
commit1037ca5a8ea001bfa2a198e08655620234e9befd (patch)
treedcf9b1966caca1eab0437f730f487701a960d851 /Tools/scripts/deepfreeze.py
parent4d6c0c0cce05befa06e0cad7351b1303ac048277 (diff)
downloadcpython-1037ca5a8ea001bfa2a198e08655620234e9befd.tar.gz
cpython-1037ca5a8ea001bfa2a198e08655620234e9befd.zip
bpo-45850: Implement deep-freeze on Windows (#29648)
Implement changes to build with deep-frozen modules on Windows. Note that we now require Python 3.10 as the "bootstrap" or "host" Python. This causes a modest startup speed (around 7%) on Windows.
Diffstat (limited to 'Tools/scripts/deepfreeze.py')
-rw-r--r--Tools/scripts/deepfreeze.py58
1 files changed, 46 insertions, 12 deletions
diff --git a/Tools/scripts/deepfreeze.py b/Tools/scripts/deepfreeze.py
index 074127f9492..b6d52b74549 100644
--- a/Tools/scripts/deepfreeze.py
+++ b/Tools/scripts/deepfreeze.py
@@ -1,13 +1,16 @@
import argparse
+import ast
import builtins
import collections
import contextlib
import os
-import sys
+import re
import time
import types
import typing
+import umarshal
+
verbose = False
@@ -55,7 +58,8 @@ def get_localsplus_counts(code: types.CodeType,
nplaincellvars += 1
elif kind & CO_FAST_FREE:
nfreevars += 1
- assert nlocals == len(code.co_varnames) == code.co_nlocals
+ assert nlocals == len(code.co_varnames) == code.co_nlocals, \
+ (nlocals, len(code.co_varnames), code.co_nlocals)
assert ncellvars == len(code.co_cellvars)
assert nfreevars == len(code.co_freevars)
assert len(names) == nlocals + nplaincellvars + nfreevars
@@ -274,14 +278,7 @@ class Printer:
self.write(item + ",")
return f"& {name}._object.ob_base.ob_base"
- def generate_int(self, name: str, i: int) -> str:
- maxint = sys.maxsize
- if maxint == 2**31 - 1:
- digit = 2**15
- elif maxint == 2**63 - 1:
- digit = 2**30
- else:
- assert False, f"What int size is this system?!? {maxint=}"
+ def _generate_int_for_bits(self, name: str, i: int, digit: int) -> None:
sign = -1 if i < 0 else 0 if i == 0 else +1
i = abs(i)
digits: list[int] = []
@@ -298,6 +295,20 @@ class Printer:
if digits:
ds = ", ".join(map(str, digits))
self.write(f".ob_digit = {{ {ds} }},")
+
+ def generate_int(self, name: str, i: int) -> str:
+ if abs(i) < 2**15:
+ self._generate_int_for_bits(name, i, 2**15)
+ else:
+ connective = "if"
+ for bits_in_digit in 15, 30:
+ self.write(f"#{connective} PYLONG_BITS_IN_DIGIT == {bits_in_digit}")
+ self._generate_int_for_bits(name, i, 2**bits_in_digit)
+ connective = "elif"
+ self.write("#else")
+ self.write('#error "PYLONG_BITS_IN_DIGIT should be 15 or 30"')
+ self.write("#endif")
+ # If neither clause applies, it won't compile
return f"& {name}.ob_base.ob_base"
def generate_float(self, name: str, x: float) -> str:
@@ -326,7 +337,7 @@ class Printer:
return self.cache[key]
self.misses += 1
match obj:
- case types.CodeType() as code:
+ case types.CodeType() | umarshal.Code() as code:
val = self.generate_code(name, code)
case tuple(t):
val = self.generate_tuple(name, t)
@@ -367,8 +378,31 @@ _Py_get_%%NAME%%_toplevel(void)
}
"""
+FROZEN_COMMENT = "/* Auto-generated by Programs/_freeze_module.c */"
+
+FROZEN_DATA_LINE = r"\s*(\d+,\s*)+\s*"
+
+
+def is_frozen_header(source: str) -> bool:
+ return source.startswith(FROZEN_COMMENT)
+
+
+def decode_frozen_data(source: str) -> types.CodeType:
+ lines = source.splitlines()
+ while lines and re.match(FROZEN_DATA_LINE, lines[0]) is None:
+ del lines[0]
+ while lines and re.match(FROZEN_DATA_LINE, lines[-1]) is None:
+ del lines[-1]
+ values: tuple[int, ...] = ast.literal_eval("".join(lines))
+ data = bytes(values)
+ return umarshal.loads(data)
+
+
def generate(source: str, filename: str, modname: str, file: typing.TextIO) -> None:
- code = compile(source, filename, "exec")
+ if is_frozen_header(source):
+ code = decode_frozen_data(source)
+ else:
+ code = compile(source, filename, "exec")
printer = Printer(file)
printer.generate("toplevel", code)
printer.write("")