22
33The script may be executed by _bootstrap_python interpreter.
44Shared library extension modules are not available in that case.
5- On Windows, and in cross-compilation cases, it is executed
6- by Python 3.10, and 3.11 features are not available .
5+ Requires 3.11+ to be executed,
6+ because relies on `code.co_qualname` and `code.co_exceptiontable` .
77"""
8+
9+ from __future__ import annotations
10+
811import argparse
912import builtins
1013import collections
1316import re
1417import time
1518import types
16- from typing import TextIO
1719
1820import umarshal
1921
22+ TYPE_CHECKING = False
23+ if TYPE_CHECKING :
24+ from collections .abc import Iterator
25+ from typing import Any , TextIO
26+
2027ROOT = os .path .dirname (os .path .dirname (os .path .dirname (__file__ )))
2128
2229verbose = False
@@ -45,8 +52,8 @@ def make_string_literal(b: bytes) -> str:
4552
4653next_code_version = 1
4754
48- def get_localsplus (code : types .CodeType ):
49- a = collections .defaultdict (int )
55+ def get_localsplus (code : types .CodeType ) -> tuple [ tuple [ str , ...], bytes ] :
56+ a : collections . defaultdict [ str , int ] = collections .defaultdict (int )
5057 for name in code .co_varnames :
5158 a [name ] |= CO_FAST_LOCAL
5259 for name in code .co_cellvars :
@@ -136,7 +143,7 @@ def get_identifiers_and_strings(self) -> tuple[set[str], dict[str, str]]:
136143 return identifiers , strings
137144
138145 @contextlib .contextmanager
139- def indent (self ) -> None :
146+ def indent (self ) -> Iterator [ None ] :
140147 save_level = self .level
141148 try :
142149 self .level += 1
@@ -148,7 +155,7 @@ def write(self, arg: str) -> None:
148155 self .file .writelines ((" " * self .level , arg , "\n " ))
149156
150157 @contextlib .contextmanager
151- def block (self , prefix : str , suffix : str = "" ) -> None :
158+ def block (self , prefix : str , suffix : str = "" ) -> Iterator [ None ] :
152159 self .write (prefix + " {" )
153160 with self .indent ():
154161 yield
@@ -250,9 +257,17 @@ def generate_code(self, name: str, code: types.CodeType) -> str:
250257 co_names = self .generate (name + "_names" , code .co_names )
251258 co_filename = self .generate (name + "_filename" , code .co_filename )
252259 co_name = self .generate (name + "_name" , code .co_name )
253- co_qualname = self .generate (name + "_qualname" , code .co_qualname )
254260 co_linetable = self .generate (name + "_linetable" , code .co_linetable )
255- co_exceptiontable = self .generate (name + "_exceptiontable" , code .co_exceptiontable )
261+ # We use 3.10 for type checking, but this module requires 3.11
262+ # TODO: bump python version for this script.
263+ co_qualname = self .generate (
264+ name + "_qualname" ,
265+ code .co_qualname , # type: ignore[attr-defined]
266+ )
267+ co_exceptiontable = self .generate (
268+ name + "_exceptiontable" ,
269+ code .co_exceptiontable , # type: ignore[attr-defined]
270+ )
256271 # These fields are not directly accessible
257272 localsplusnames , localspluskinds = get_localsplus (code )
258273 co_localsplusnames = self .generate (name + "_localsplusnames" , localsplusnames )
@@ -379,13 +394,13 @@ def generate_complex(self, name: str, z: complex) -> str:
379394 self .write (f".cval = {{ { z .real } , { z .imag } }}," )
380395 return f"&{ name } .ob_base"
381396
382- def generate_frozenset (self , name : str , fs : frozenset [object ]) -> str :
397+ def generate_frozenset (self , name : str , fs : frozenset [Any ]) -> str :
383398 try :
384- fs = sorted (fs )
399+ fs_sorted = sorted (fs )
385400 except TypeError :
386401 # frozen set with incompatible types, fallback to repr()
387- fs = sorted (fs , key = repr )
388- ret = self .generate_tuple (name , tuple (fs ))
402+ fs_sorted = sorted (fs , key = repr )
403+ ret = self .generate_tuple (name , tuple (fs_sorted ))
389404 self .write ("// TODO: The above tuple should be a frozenset" )
390405 return ret
391406
@@ -402,7 +417,7 @@ def generate(self, name: str, obj: object) -> str:
402417 # print(f"Cache hit {key!r:.40}: {self.cache[key]!r:.40}")
403418 return self .cache [key ]
404419 self .misses += 1
405- if isinstance (obj , ( types .CodeType , umarshal . Code ) ) :
420+ if isinstance (obj , types .CodeType ) :
406421 val = self .generate_code (name , obj )
407422 elif isinstance (obj , tuple ):
408423 val = self .generate_tuple (name , obj )
@@ -458,7 +473,7 @@ def decode_frozen_data(source: str) -> types.CodeType:
458473 if re .match (FROZEN_DATA_LINE , line ):
459474 values .extend ([int (x ) for x in line .split ("," ) if x .strip ()])
460475 data = bytes (values )
461- return umarshal .loads (data )
476+ return umarshal .loads (data ) # type: ignore[no-any-return]
462477
463478
464479def generate (args : list [str ], output : TextIO ) -> None :
@@ -494,12 +509,12 @@ def generate(args: list[str], output: TextIO) -> None:
494509 help = "Input file and module name (required) in file:modname format" )
495510
496511@contextlib .contextmanager
497- def report_time (label : str ):
498- t0 = time .time ()
512+ def report_time (label : str ) -> Iterator [ None ] :
513+ t0 = time .perf_counter ()
499514 try :
500515 yield
501516 finally :
502- t1 = time .time ()
517+ t1 = time .perf_counter ()
503518 if verbose :
504519 print (f"{ label } : { t1 - t0 :.3f} sec" )
505520
0 commit comments