Skip to content

Commit 247480a

Browse files
bpo-46541: Generate the global objects initializer. (gh-30941)
This change is a prerequisite for generating code for other global objects (like strings in gh-30928). (We borrowed some code from Tools/scripts/deepfreeze.py.) https://bugs.python.org/issue46541
1 parent 183f8d5 commit 247480a

File tree

3 files changed

+136
-1
lines changed

3 files changed

+136
-1
lines changed

Include/internal/pycore_runtime_init.h

+3
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ extern "C" {
9494
_PyBytes_SIMPLE_INIT(CH, 1) \
9595
}
9696

97+
98+
/* The following is auto-generated by Tools/scripts/generate_global_objects.py. */
9799
#define _Py_global_objects_INIT { \
98100
.singletons = { \
99101
.small_ints = { \
@@ -622,6 +624,7 @@ extern "C" {
622624
}, \
623625
}, \
624626
}
627+
/* End auto-generated code */
625628

626629

627630
#ifdef __cplusplus

Makefile.pre.in

+9-1
Original file line numberDiff line numberDiff line change
@@ -1172,6 +1172,13 @@ Python/deepfreeze/deepfreeze.c: $(DEEPFREEZE_DEPS)
11721172
.PHONY: regen-importlib
11731173
regen-importlib: regen-frozen
11741174

1175+
############################################################################
1176+
# Global objects
1177+
1178+
.PHONY: regen-global-objects
1179+
regen-global-objects: $(srcdir)/Tools/scripts/generate_global_objects.py $(FREEZE_MODULE_DEPS)
1180+
$(PYTHON_FOR_FREEZE) $(srcdir)/Tools/scripts/generate_global_objects.py
1181+
11751182
############################################################################
11761183
# ABI
11771184

@@ -1183,7 +1190,8 @@ regen-limited-abi: all
11831190

11841191
regen-all: regen-opcode regen-opcode-targets regen-typeslots \
11851192
regen-token regen-ast regen-keyword regen-frozen clinic \
1186-
regen-pegen-metaparser regen-pegen regen-test-frozenmain
1193+
regen-pegen-metaparser regen-pegen regen-test-frozenmain \
1194+
regen-global-objects
11871195
@echo
11881196
@echo "Note: make regen-stdlib-module-names and make autoconf should be run manually"
11891197

+124
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
import argparse
2+
import ast
3+
import builtins
4+
import collections
5+
import contextlib
6+
import os.path
7+
import sys
8+
9+
10+
assert os.path.isabs(__file__), __file__
11+
ROOT = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
12+
INTERNAL = os.path.join(ROOT, 'Include', 'internal')
13+
14+
15+
#######################################
16+
# helpers
17+
18+
def iter_to_marker(lines, marker):
19+
for line in lines:
20+
if line.rstrip() == marker:
21+
break
22+
yield line
23+
24+
25+
class Printer:
26+
27+
def __init__(self, file):
28+
self.level = 0
29+
self.file = file
30+
self.continuation = [False]
31+
32+
@contextlib.contextmanager
33+
def indent(self):
34+
save_level = self.level
35+
try:
36+
self.level += 1
37+
yield
38+
finally:
39+
self.level = save_level
40+
41+
def write(self, arg):
42+
eol = '\n'
43+
if self.continuation[-1]:
44+
eol = f' \\{eol}' if arg else f'\\{eol}'
45+
self.file.writelines((" "*self.level, arg, eol))
46+
47+
@contextlib.contextmanager
48+
def block(self, prefix, suffix="", *, continuation=None):
49+
if continuation is None:
50+
continuation = self.continuation[-1]
51+
self.continuation.append(continuation)
52+
53+
self.write(prefix + " {")
54+
with self.indent():
55+
yield
56+
self.continuation.pop()
57+
self.write("}" + suffix)
58+
59+
60+
#######################################
61+
# the global objects
62+
63+
START = '/* The following is auto-generated by Tools/scripts/generate_global_objects.py. */'
64+
END = '/* End auto-generated code */'
65+
66+
67+
def generate_runtime_init():
68+
# First get some info from the declarations.
69+
nsmallposints = None
70+
nsmallnegints = None
71+
with open(os.path.join(INTERNAL, 'pycore_global_objects.h')) as infile:
72+
for line in infile:
73+
if line.startswith('#define _PY_NSMALLPOSINTS'):
74+
nsmallposints = int(line.split()[-1])
75+
elif line.startswith('#define _PY_NSMALLNEGINTS'):
76+
nsmallnegints = int(line.split()[-1])
77+
break
78+
else:
79+
raise NotImplementedError
80+
assert nsmallposints and nsmallnegints
81+
82+
# Then target the runtime initializer.
83+
filename = os.path.join(INTERNAL, 'pycore_runtime_init.h')
84+
85+
# Read the non-generated part of the file.
86+
with open(filename) as infile:
87+
before = ''.join(iter_to_marker(infile, START))[:-1]
88+
for _ in iter_to_marker(infile, END):
89+
pass
90+
after = infile.read()[:-1]
91+
92+
# Generate the file.
93+
with open(filename, 'w', encoding='utf-8') as outfile:
94+
printer = Printer(outfile)
95+
printer.write(before)
96+
printer.write(START)
97+
with printer.block('#define _Py_global_objects_INIT', continuation=True):
98+
with printer.block('.singletons =', ','):
99+
# Global int objects.
100+
with printer.block('.small_ints =', ','):
101+
for i in range(-nsmallnegints, nsmallposints):
102+
printer.write(f'_PyLong_DIGIT_INIT({i}),')
103+
printer.write('')
104+
# Global bytes objects.
105+
printer.write('.bytes_empty = _PyBytes_SIMPLE_INIT(0, 0),')
106+
with printer.block('.bytes_characters =', ','):
107+
for i in range(256):
108+
printer.write(f'_PyBytes_CHAR_INIT({i}),')
109+
printer.write(END)
110+
printer.write(after)
111+
112+
113+
#######################################
114+
# the script
115+
116+
def main() -> None:
117+
generate_runtime_init()
118+
119+
120+
if __name__ == '__main__':
121+
argv = sys.argv[1:]
122+
if argv:
123+
sys.exit(f'ERROR: got unexpected args {argv}')
124+
main()

0 commit comments

Comments
 (0)