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 Dict , FrozenSet , TextIO , Tuple
17-
1819import umarshal
1920
21+ TYPE_CHECKING = False
22+ if TYPE_CHECKING :
23+ from collections .abc import Iterator
24+ from typing import Any , TextIO , Dict , FrozenSet , TextIO , Tuple
25+
2026ROOT = os .path .dirname (os .path .dirname (os .path .dirname (__file__ )))
2127
2228verbose = False
@@ -45,8 +51,8 @@ def make_string_literal(b: bytes) -> str:
4551
4652next_code_version = 1
4753
48- def get_localsplus (code : types .CodeType ):
49- a = collections .defaultdict (int )
54+ def get_localsplus (code : types .CodeType ) -> tuple [ tuple [ str , ...], bytes ] :
55+ a : collections . defaultdict [ str , int ] = collections .defaultdict (int )
5056 for name in code .co_varnames :
5157 a [name ] |= CO_FAST_LOCAL
5258 for name in code .co_cellvars :
@@ -58,7 +64,7 @@ def get_localsplus(code: types.CodeType):
5864
5965def get_localsplus_counts (code : types .CodeType ,
6066 names : Tuple [str , ...],
61- kinds : bytes ) -> Tuple [int , int , int , int ]:
67+ kinds : bytes ) -> Tuple [int , int , int ]:
6268 nlocals = 0
6369 ncellvars = 0
6470 nfreevars = 0
@@ -136,7 +142,7 @@ def get_identifiers_and_strings(self) -> tuple[set[str], dict[str, str]]:
136142 return identifiers , strings
137143
138144 @contextlib .contextmanager
139- def indent (self ) -> None :
145+ def indent (self ) -> Iterator [ None ] :
140146 save_level = self .level
141147 try :
142148 self .level += 1
@@ -148,7 +154,7 @@ def write(self, arg: str) -> None:
148154 self .file .writelines ((" " * self .level , arg , "\n " ))
149155
150156 @contextlib .contextmanager
151- def block (self , prefix : str , suffix : str = "" ) -> None :
157+ def block (self , prefix : str , suffix : str = "" ) -> Iterator [ None ] :
152158 self .write (prefix + " {" )
153159 with self .indent ():
154160 yield
@@ -250,9 +256,17 @@ def generate_code(self, name: str, code: types.CodeType) -> str:
250256 co_names = self .generate (name + "_names" , code .co_names )
251257 co_filename = self .generate (name + "_filename" , code .co_filename )
252258 co_name = self .generate (name + "_name" , code .co_name )
253- co_qualname = self .generate (name + "_qualname" , code .co_qualname )
254259 co_linetable = self .generate (name + "_linetable" , code .co_linetable )
255- co_exceptiontable = self .generate (name + "_exceptiontable" , code .co_exceptiontable )
260+ # We use 3.10 for type checking, but this module requires 3.11
261+ # TODO: bump python version for this script.
262+ co_qualname = self .generate (
263+ name + "_qualname" ,
264+ code .co_qualname , # type: ignore[attr-defined]
265+ )
266+ co_exceptiontable = self .generate (
267+ name + "_exceptiontable" ,
268+ code .co_exceptiontable , # type: ignore[attr-defined]
269+ )
256270 # These fields are not directly accessible
257271 localsplusnames , localspluskinds = get_localsplus (code )
258272 co_localsplusnames = self .generate (name + "_localsplusnames" , localsplusnames )
@@ -379,13 +393,13 @@ def generate_complex(self, name: str, z: complex) -> str:
379393 self .write (f".cval = {{ { z .real } , { z .imag } }}," )
380394 return f"&{ name } .ob_base"
381395
382- def generate_frozenset (self , name : str , fs : FrozenSet [object ]) -> str :
396+ def generate_frozenset (self , name : str , fs : FrozenSet [Any ]) -> str :
383397 try :
384- fs = sorted (fs )
398+ fs_sorted = sorted (fs )
385399 except TypeError :
386400 # frozen set with incompatible types, fallback to repr()
387- fs = sorted (fs , key = repr )
388- ret = self .generate_tuple (name , tuple (fs ))
401+ fs_sorted = sorted (fs , key = repr )
402+ ret = self .generate_tuple (name , tuple (fs_sorted ))
389403 self .write ("// TODO: The above tuple should be a frozenset" )
390404 return ret
391405
@@ -402,7 +416,7 @@ def generate(self, name: str, obj: object) -> str:
402416 # print(f"Cache hit {key!r:.40}: {self.cache[key]!r:.40}")
403417 return self .cache [key ]
404418 self .misses += 1
405- if isinstance (obj , ( types .CodeType , umarshal . Code ) ) :
419+ if isinstance (obj , types .CodeType ) :
406420 val = self .generate_code (name , obj )
407421 elif isinstance (obj , tuple ):
408422 val = self .generate_tuple (name , obj )
@@ -458,7 +472,7 @@ def decode_frozen_data(source: str) -> types.CodeType:
458472 if re .match (FROZEN_DATA_LINE , line ):
459473 values .extend ([int (x ) for x in line .split ("," ) if x .strip ()])
460474 data = bytes (values )
461- return umarshal .loads (data )
475+ return umarshal .loads (data ) # type: ignore[no-any-return]
462476
463477
464478def generate (args : list [str ], output : TextIO ) -> None :
@@ -494,12 +508,12 @@ def generate(args: list[str], output: TextIO) -> None:
494508 help = "Input file and module name (required) in file:modname format" )
495509
496510@contextlib .contextmanager
497- def report_time (label : str ):
498- t0 = time .time ()
511+ def report_time (label : str ) -> Iterator [ None ] :
512+ t0 = time .perf_counter ()
499513 try :
500514 yield
501515 finally :
502- t1 = time .time ()
516+ t1 = time .perf_counter ()
503517 if verbose :
504518 print (f"{ label } : { t1 - t0 :.3f} sec" )
505519
0 commit comments