mirror of
https://github.com/python/cpython.git
synced 2025-08-04 08:59:19 +00:00
gh-133403: Check Tools/build/deepfreeze.py
with mypy (#133802)
This commit is contained in:
parent
c3a1da5b93
commit
7eaa097390
5 changed files with 45 additions and 25 deletions
2
.github/workflows/mypy.yml
vendored
2
.github/workflows/mypy.yml
vendored
|
@ -14,10 +14,12 @@ on:
|
||||||
- "Lib/tomllib/**"
|
- "Lib/tomllib/**"
|
||||||
- "Misc/mypy/**"
|
- "Misc/mypy/**"
|
||||||
- "Tools/build/compute-changes.py"
|
- "Tools/build/compute-changes.py"
|
||||||
|
- "Tools/build/deepfreeze.py"
|
||||||
- "Tools/build/generate_sbom.py"
|
- "Tools/build/generate_sbom.py"
|
||||||
- "Tools/build/generate-build-details.py"
|
- "Tools/build/generate-build-details.py"
|
||||||
- "Tools/build/verify_ensurepip_wheels.py"
|
- "Tools/build/verify_ensurepip_wheels.py"
|
||||||
- "Tools/build/update_file.py"
|
- "Tools/build/update_file.py"
|
||||||
|
- "Tools/build/umarshal.py"
|
||||||
- "Tools/cases_generator/**"
|
- "Tools/cases_generator/**"
|
||||||
- "Tools/clinic/**"
|
- "Tools/clinic/**"
|
||||||
- "Tools/jit/**"
|
- "Tools/jit/**"
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
extend = "../../.ruff.toml" # Inherit the project-wide settings
|
extend = "../../.ruff.toml" # Inherit the project-wide settings
|
||||||
|
|
||||||
[per-file-target-version]
|
[per-file-target-version]
|
||||||
"deepfreeze.py" = "py310"
|
"deepfreeze.py" = "py311" # requires `code.co_exceptiontable`
|
||||||
"stable_abi.py" = "py311" # requires 'tomllib'
|
"stable_abi.py" = "py311" # requires 'tomllib'
|
||||||
|
|
||||||
[format]
|
[format]
|
||||||
|
|
|
@ -2,9 +2,12 @@
|
||||||
|
|
||||||
The script may be executed by _bootstrap_python interpreter.
|
The script may be executed by _bootstrap_python interpreter.
|
||||||
Shared library extension modules are not available in that case.
|
Shared library extension modules are not available in that case.
|
||||||
On Windows, and in cross-compilation cases, it is executed
|
Requires 3.11+ to be executed,
|
||||||
by Python 3.10, and 3.11 features are not available.
|
because relies on `code.co_qualname` and `code.co_exceptiontable`.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import builtins
|
import builtins
|
||||||
import collections
|
import collections
|
||||||
|
@ -13,10 +16,14 @@ import os
|
||||||
import re
|
import re
|
||||||
import time
|
import time
|
||||||
import types
|
import types
|
||||||
from typing import TextIO
|
|
||||||
|
|
||||||
import umarshal
|
import umarshal
|
||||||
|
|
||||||
|
TYPE_CHECKING = False
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from collections.abc import Iterator
|
||||||
|
from typing import Any, TextIO
|
||||||
|
|
||||||
ROOT = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
|
ROOT = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
|
||||||
|
|
||||||
verbose = False
|
verbose = False
|
||||||
|
@ -45,8 +52,8 @@ CO_FAST_FREE = 0x80
|
||||||
|
|
||||||
next_code_version = 1
|
next_code_version = 1
|
||||||
|
|
||||||
def get_localsplus(code: types.CodeType):
|
def get_localsplus(code: types.CodeType) -> tuple[tuple[str, ...], bytes]:
|
||||||
a = collections.defaultdict(int)
|
a: collections.defaultdict[str, int] = collections.defaultdict(int)
|
||||||
for name in code.co_varnames:
|
for name in code.co_varnames:
|
||||||
a[name] |= CO_FAST_LOCAL
|
a[name] |= CO_FAST_LOCAL
|
||||||
for name in code.co_cellvars:
|
for name in code.co_cellvars:
|
||||||
|
@ -136,7 +143,7 @@ class Printer:
|
||||||
return identifiers, strings
|
return identifiers, strings
|
||||||
|
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
def indent(self) -> None:
|
def indent(self) -> Iterator[None]:
|
||||||
save_level = self.level
|
save_level = self.level
|
||||||
try:
|
try:
|
||||||
self.level += 1
|
self.level += 1
|
||||||
|
@ -148,7 +155,7 @@ class Printer:
|
||||||
self.file.writelines((" "*self.level, arg, "\n"))
|
self.file.writelines((" "*self.level, arg, "\n"))
|
||||||
|
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
def block(self, prefix: str, suffix: str = "") -> None:
|
def block(self, prefix: str, suffix: str = "") -> Iterator[None]:
|
||||||
self.write(prefix + " {")
|
self.write(prefix + " {")
|
||||||
with self.indent():
|
with self.indent():
|
||||||
yield
|
yield
|
||||||
|
@ -250,9 +257,17 @@ class Printer:
|
||||||
co_names = self.generate(name + "_names", code.co_names)
|
co_names = self.generate(name + "_names", code.co_names)
|
||||||
co_filename = self.generate(name + "_filename", code.co_filename)
|
co_filename = self.generate(name + "_filename", code.co_filename)
|
||||||
co_name = self.generate(name + "_name", code.co_name)
|
co_name = self.generate(name + "_name", code.co_name)
|
||||||
co_qualname = self.generate(name + "_qualname", code.co_qualname)
|
|
||||||
co_linetable = self.generate(name + "_linetable", code.co_linetable)
|
co_linetable = self.generate(name + "_linetable", code.co_linetable)
|
||||||
co_exceptiontable = self.generate(name + "_exceptiontable", code.co_exceptiontable)
|
# We use 3.10 for type checking, but this module requires 3.11
|
||||||
|
# TODO: bump python version for this script.
|
||||||
|
co_qualname = self.generate(
|
||||||
|
name + "_qualname",
|
||||||
|
code.co_qualname, # type: ignore[attr-defined]
|
||||||
|
)
|
||||||
|
co_exceptiontable = self.generate(
|
||||||
|
name + "_exceptiontable",
|
||||||
|
code.co_exceptiontable, # type: ignore[attr-defined]
|
||||||
|
)
|
||||||
# These fields are not directly accessible
|
# These fields are not directly accessible
|
||||||
localsplusnames, localspluskinds = get_localsplus(code)
|
localsplusnames, localspluskinds = get_localsplus(code)
|
||||||
co_localsplusnames = self.generate(name + "_localsplusnames", localsplusnames)
|
co_localsplusnames = self.generate(name + "_localsplusnames", localsplusnames)
|
||||||
|
@ -379,13 +394,13 @@ class Printer:
|
||||||
self.write(f".cval = {{ {z.real}, {z.imag} }},")
|
self.write(f".cval = {{ {z.real}, {z.imag} }},")
|
||||||
return f"&{name}.ob_base"
|
return f"&{name}.ob_base"
|
||||||
|
|
||||||
def generate_frozenset(self, name: str, fs: frozenset[object]) -> str:
|
def generate_frozenset(self, name: str, fs: frozenset[Any]) -> str:
|
||||||
try:
|
try:
|
||||||
fs = sorted(fs)
|
fs_sorted = sorted(fs)
|
||||||
except TypeError:
|
except TypeError:
|
||||||
# frozen set with incompatible types, fallback to repr()
|
# frozen set with incompatible types, fallback to repr()
|
||||||
fs = sorted(fs, key=repr)
|
fs_sorted = sorted(fs, key=repr)
|
||||||
ret = self.generate_tuple(name, tuple(fs))
|
ret = self.generate_tuple(name, tuple(fs_sorted))
|
||||||
self.write("// TODO: The above tuple should be a frozenset")
|
self.write("// TODO: The above tuple should be a frozenset")
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
@ -402,7 +417,7 @@ class Printer:
|
||||||
# print(f"Cache hit {key!r:.40}: {self.cache[key]!r:.40}")
|
# print(f"Cache hit {key!r:.40}: {self.cache[key]!r:.40}")
|
||||||
return self.cache[key]
|
return self.cache[key]
|
||||||
self.misses += 1
|
self.misses += 1
|
||||||
if isinstance(obj, (types.CodeType, umarshal.Code)) :
|
if isinstance(obj, types.CodeType) :
|
||||||
val = self.generate_code(name, obj)
|
val = self.generate_code(name, obj)
|
||||||
elif isinstance(obj, tuple):
|
elif isinstance(obj, tuple):
|
||||||
val = self.generate_tuple(name, obj)
|
val = self.generate_tuple(name, obj)
|
||||||
|
@ -458,7 +473,7 @@ def decode_frozen_data(source: str) -> types.CodeType:
|
||||||
if re.match(FROZEN_DATA_LINE, line):
|
if re.match(FROZEN_DATA_LINE, line):
|
||||||
values.extend([int(x) for x in line.split(",") if x.strip()])
|
values.extend([int(x) for x in line.split(",") if x.strip()])
|
||||||
data = bytes(values)
|
data = bytes(values)
|
||||||
return umarshal.loads(data)
|
return umarshal.loads(data) # type: ignore[no-any-return]
|
||||||
|
|
||||||
|
|
||||||
def generate(args: list[str], output: TextIO) -> None:
|
def generate(args: list[str], output: TextIO) -> None:
|
||||||
|
@ -494,12 +509,12 @@ group.add_argument('args', nargs="*", default=(),
|
||||||
help="Input file and module name (required) in file:modname format")
|
help="Input file and module name (required) in file:modname format")
|
||||||
|
|
||||||
@contextlib.contextmanager
|
@contextlib.contextmanager
|
||||||
def report_time(label: str):
|
def report_time(label: str) -> Iterator[None]:
|
||||||
t0 = time.time()
|
t0 = time.perf_counter()
|
||||||
try:
|
try:
|
||||||
yield
|
yield
|
||||||
finally:
|
finally:
|
||||||
t1 = time.time()
|
t1 = time.perf_counter()
|
||||||
if verbose:
|
if verbose:
|
||||||
print(f"{label}: {t1-t0:.3f} sec")
|
print(f"{label}: {t1-t0:.3f} sec")
|
||||||
|
|
||||||
|
|
|
@ -4,10 +4,12 @@
|
||||||
# .github/workflows/mypy.yml
|
# .github/workflows/mypy.yml
|
||||||
files =
|
files =
|
||||||
Tools/build/compute-changes.py,
|
Tools/build/compute-changes.py,
|
||||||
|
Tools/build/deepfreeze.py,
|
||||||
Tools/build/generate-build-details.py,
|
Tools/build/generate-build-details.py,
|
||||||
Tools/build/generate_sbom.py,
|
Tools/build/generate_sbom.py,
|
||||||
Tools/build/verify_ensurepip_wheels.py,
|
Tools/build/verify_ensurepip_wheels.py,
|
||||||
Tools/build/update_file.py
|
Tools/build/update_file.py,
|
||||||
|
Tools/build/umarshal.py
|
||||||
|
|
||||||
pretty = True
|
pretty = True
|
||||||
|
|
||||||
|
|
|
@ -145,12 +145,12 @@ class Reader:
|
||||||
def r_float_bin(self) -> float:
|
def r_float_bin(self) -> float:
|
||||||
buf = self.r_string(8)
|
buf = self.r_string(8)
|
||||||
import struct # Lazy import to avoid breaking UNIX build
|
import struct # Lazy import to avoid breaking UNIX build
|
||||||
return struct.unpack("d", buf)[0]
|
return struct.unpack("d", buf)[0] # type: ignore[no-any-return]
|
||||||
|
|
||||||
def r_float_str(self) -> float:
|
def r_float_str(self) -> float:
|
||||||
n = self.r_byte()
|
n = self.r_byte()
|
||||||
buf = self.r_string(n)
|
buf = self.r_string(n)
|
||||||
return ast.literal_eval(buf.decode("ascii"))
|
return ast.literal_eval(buf.decode("ascii")) # type: ignore[no-any-return]
|
||||||
|
|
||||||
def r_ref_reserve(self, flag: int) -> int:
|
def r_ref_reserve(self, flag: int) -> int:
|
||||||
if flag:
|
if flag:
|
||||||
|
@ -306,7 +306,7 @@ def loads(data: bytes) -> Any:
|
||||||
return r.r_object()
|
return r.r_object()
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main() -> None:
|
||||||
# Test
|
# Test
|
||||||
import marshal
|
import marshal
|
||||||
import pprint
|
import pprint
|
||||||
|
@ -314,8 +314,9 @@ def main():
|
||||||
data = marshal.dumps(sample)
|
data = marshal.dumps(sample)
|
||||||
retval = loads(data)
|
retval = loads(data)
|
||||||
assert retval == sample, retval
|
assert retval == sample, retval
|
||||||
sample = main.__code__
|
|
||||||
data = marshal.dumps(sample)
|
sample2 = main.__code__
|
||||||
|
data = marshal.dumps(sample2)
|
||||||
retval = loads(data)
|
retval = loads(data)
|
||||||
assert isinstance(retval, Code), retval
|
assert isinstance(retval, Code), retval
|
||||||
pprint.pprint(retval.__dict__)
|
pprint.pprint(retval.__dict__)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue