2
2
3
3
The script may be executed by _bootstrap_python interpreter.
4
4
Shared 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` .
7
7
"""
8
+
9
+ from __future__ import annotations
10
+
8
11
import argparse
9
12
import builtins
10
13
import collections
13
16
import re
14
17
import time
15
18
import types
16
- from typing import TextIO
17
19
18
20
import umarshal
19
21
22
+ TYPE_CHECKING = False
23
+ if TYPE_CHECKING :
24
+ from collections .abc import Iterator
25
+ from typing import Any , TextIO
26
+
20
27
ROOT = os .path .dirname (os .path .dirname (os .path .dirname (__file__ )))
21
28
22
29
verbose = False
@@ -45,8 +52,8 @@ def make_string_literal(b: bytes) -> str:
45
52
46
53
next_code_version = 1
47
54
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 )
50
57
for name in code .co_varnames :
51
58
a [name ] |= CO_FAST_LOCAL
52
59
for name in code .co_cellvars :
@@ -136,7 +143,7 @@ def get_identifiers_and_strings(self) -> tuple[set[str], dict[str, str]]:
136
143
return identifiers , strings
137
144
138
145
@contextlib .contextmanager
139
- def indent (self ) -> None :
146
+ def indent (self ) -> Iterator [ None ] :
140
147
save_level = self .level
141
148
try :
142
149
self .level += 1
@@ -148,7 +155,7 @@ def write(self, arg: str) -> None:
148
155
self .file .writelines ((" " * self .level , arg , "\n " ))
149
156
150
157
@contextlib .contextmanager
151
- def block (self , prefix : str , suffix : str = "" ) -> None :
158
+ def block (self , prefix : str , suffix : str = "" ) -> Iterator [ None ] :
152
159
self .write (prefix + " {" )
153
160
with self .indent ():
154
161
yield
@@ -250,9 +257,17 @@ def generate_code(self, name: str, code: types.CodeType) -> str:
250
257
co_names = self .generate (name + "_names" , code .co_names )
251
258
co_filename = self .generate (name + "_filename" , code .co_filename )
252
259
co_name = self .generate (name + "_name" , code .co_name )
253
- co_qualname = self .generate (name + "_qualname" , code .co_qualname )
254
260
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
+ )
256
271
# These fields are not directly accessible
257
272
localsplusnames , localspluskinds = get_localsplus (code )
258
273
co_localsplusnames = self .generate (name + "_localsplusnames" , localsplusnames )
@@ -379,13 +394,13 @@ def generate_complex(self, name: str, z: complex) -> str:
379
394
self .write (f".cval = {{ { z .real } , { z .imag } }}," )
380
395
return f"&{ name } .ob_base"
381
396
382
- def generate_frozenset (self , name : str , fs : frozenset [object ]) -> str :
397
+ def generate_frozenset (self , name : str , fs : frozenset [Any ]) -> str :
383
398
try :
384
- fs = sorted (fs )
399
+ fs_sorted = sorted (fs )
385
400
except TypeError :
386
401
# 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 ))
389
404
self .write ("// TODO: The above tuple should be a frozenset" )
390
405
return ret
391
406
@@ -402,7 +417,7 @@ def generate(self, name: str, obj: object) -> str:
402
417
# print(f"Cache hit {key!r:.40}: {self.cache[key]!r:.40}")
403
418
return self .cache [key ]
404
419
self .misses += 1
405
- if isinstance (obj , ( types .CodeType , umarshal . Code ) ) :
420
+ if isinstance (obj , types .CodeType ) :
406
421
val = self .generate_code (name , obj )
407
422
elif isinstance (obj , tuple ):
408
423
val = self .generate_tuple (name , obj )
@@ -458,7 +473,7 @@ def decode_frozen_data(source: str) -> types.CodeType:
458
473
if re .match (FROZEN_DATA_LINE , line ):
459
474
values .extend ([int (x ) for x in line .split ("," ) if x .strip ()])
460
475
data = bytes (values )
461
- return umarshal .loads (data )
476
+ return umarshal .loads (data ) # type: ignore[no-any-return]
462
477
463
478
464
479
def generate (args : list [str ], output : TextIO ) -> None :
@@ -494,12 +509,12 @@ def generate(args: list[str], output: TextIO) -> None:
494
509
help = "Input file and module name (required) in file:modname format" )
495
510
496
511
@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 ()
499
514
try :
500
515
yield
501
516
finally :
502
- t1 = time .time ()
517
+ t1 = time .perf_counter ()
503
518
if verbose :
504
519
print (f"{ label } : { t1 - t0 :.3f} sec" )
505
520
0 commit comments