cinderx 2026.1.16.2__cp314-cp314-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- __static__/__init__.py +641 -0
- __static__/compiler_flags.py +8 -0
- __static__/enum.py +160 -0
- __static__/native_utils.py +77 -0
- __static__/type_code.py +48 -0
- __strict__/__init__.py +39 -0
- _cinderx.so +0 -0
- cinderx/__init__.py +577 -0
- cinderx/__pycache__/__init__.cpython-314.pyc +0 -0
- cinderx/_asyncio.py +156 -0
- cinderx/compileall.py +710 -0
- cinderx/compiler/__init__.py +40 -0
- cinderx/compiler/__main__.py +137 -0
- cinderx/compiler/config.py +7 -0
- cinderx/compiler/consts.py +72 -0
- cinderx/compiler/debug.py +70 -0
- cinderx/compiler/dis_stable.py +283 -0
- cinderx/compiler/errors.py +151 -0
- cinderx/compiler/flow_graph_optimizer.py +1287 -0
- cinderx/compiler/future.py +91 -0
- cinderx/compiler/misc.py +32 -0
- cinderx/compiler/opcode_cinder.py +18 -0
- cinderx/compiler/opcode_static.py +100 -0
- cinderx/compiler/opcodebase.py +158 -0
- cinderx/compiler/opcodes.py +991 -0
- cinderx/compiler/optimizer.py +547 -0
- cinderx/compiler/pyassem.py +3711 -0
- cinderx/compiler/pycodegen.py +7660 -0
- cinderx/compiler/pysourceloader.py +62 -0
- cinderx/compiler/static/__init__.py +1404 -0
- cinderx/compiler/static/compiler.py +629 -0
- cinderx/compiler/static/declaration_visitor.py +335 -0
- cinderx/compiler/static/definite_assignment_checker.py +280 -0
- cinderx/compiler/static/effects.py +160 -0
- cinderx/compiler/static/module_table.py +666 -0
- cinderx/compiler/static/type_binder.py +2176 -0
- cinderx/compiler/static/types.py +10580 -0
- cinderx/compiler/static/util.py +81 -0
- cinderx/compiler/static/visitor.py +91 -0
- cinderx/compiler/strict/__init__.py +69 -0
- cinderx/compiler/strict/class_conflict_checker.py +249 -0
- cinderx/compiler/strict/code_gen_base.py +409 -0
- cinderx/compiler/strict/common.py +507 -0
- cinderx/compiler/strict/compiler.py +352 -0
- cinderx/compiler/strict/feature_extractor.py +130 -0
- cinderx/compiler/strict/flag_extractor.py +97 -0
- cinderx/compiler/strict/loader.py +827 -0
- cinderx/compiler/strict/preprocessor.py +11 -0
- cinderx/compiler/strict/rewriter/__init__.py +5 -0
- cinderx/compiler/strict/rewriter/remove_annotations.py +84 -0
- cinderx/compiler/strict/rewriter/rewriter.py +975 -0
- cinderx/compiler/strict/runtime.py +77 -0
- cinderx/compiler/symbols.py +1754 -0
- cinderx/compiler/unparse.py +414 -0
- cinderx/compiler/visitor.py +194 -0
- cinderx/jit.py +230 -0
- cinderx/opcode.py +202 -0
- cinderx/static.py +113 -0
- cinderx/strictmodule.py +6 -0
- cinderx/test_support.py +341 -0
- cinderx-2026.1.16.2.dist-info/METADATA +15 -0
- cinderx-2026.1.16.2.dist-info/RECORD +68 -0
- cinderx-2026.1.16.2.dist-info/WHEEL +6 -0
- cinderx-2026.1.16.2.dist-info/licenses/LICENSE +21 -0
- cinderx-2026.1.16.2.dist-info/top_level.txt +5 -0
- opcodes/__init__.py +0 -0
- opcodes/assign_opcode_numbers.py +272 -0
- opcodes/cinderx_opcodes.py +121 -0
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
2
|
+
|
|
3
|
+
# pyre-strict
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import ast
|
|
8
|
+
import builtins
|
|
9
|
+
import sys
|
|
10
|
+
from ast import (
|
|
11
|
+
AST,
|
|
12
|
+
AsyncFunctionDef,
|
|
13
|
+
Call,
|
|
14
|
+
ClassDef,
|
|
15
|
+
expr,
|
|
16
|
+
FunctionDef,
|
|
17
|
+
ImportFrom,
|
|
18
|
+
Name,
|
|
19
|
+
NodeVisitor,
|
|
20
|
+
stmt,
|
|
21
|
+
)
|
|
22
|
+
from typing import Any, cast, final, Mapping
|
|
23
|
+
|
|
24
|
+
from ..consts import CO_FUTURE_ANNOTATIONS
|
|
25
|
+
from ..pyassem import PyFlowGraph
|
|
26
|
+
from ..pycodegen import CinderCodeGenBase, CodeGenerator, find_futures, FOR_LOOP
|
|
27
|
+
from ..symbols import BaseSymbolVisitor, FunctionScope
|
|
28
|
+
from .common import FIXED_MODULES, lineinfo
|
|
29
|
+
from .feature_extractor import _IMPLICIT_GLOBALS, FeatureExtractor
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def is_mutable(node: AST) -> bool:
|
|
33
|
+
return isinstance(node, Name) and node.id in ("mutable")
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class FindClassDef(NodeVisitor):
|
|
37
|
+
def __init__(self) -> None:
|
|
38
|
+
self.has_class = False
|
|
39
|
+
|
|
40
|
+
def check(self, node: AST) -> None:
|
|
41
|
+
self.generic_visit(node)
|
|
42
|
+
|
|
43
|
+
def visit_ClassDef(self, node: ClassDef) -> None:
|
|
44
|
+
for n in node.decorator_list:
|
|
45
|
+
if is_mutable(n):
|
|
46
|
+
# do not consider this class for freezing
|
|
47
|
+
break
|
|
48
|
+
else:
|
|
49
|
+
self.has_class = True
|
|
50
|
+
for body_stmt in node.body:
|
|
51
|
+
# also consider inner classes
|
|
52
|
+
self.visit(body_stmt)
|
|
53
|
+
|
|
54
|
+
def visit_FunctionDef(self, node: FunctionDef) -> None:
|
|
55
|
+
# do not go into func body
|
|
56
|
+
pass
|
|
57
|
+
|
|
58
|
+
def visit_AsyncFunctionDef(self, node: AsyncFunctionDef) -> None:
|
|
59
|
+
# do not go into func body
|
|
60
|
+
pass
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class ForBodyHook(ast.stmt):
|
|
64
|
+
def __init__(self, node: list[ast.stmt], target: ast.expr) -> None:
|
|
65
|
+
self.body: list[ast.stmt] = node
|
|
66
|
+
self.target = target
|
|
67
|
+
lineinfo(self, target)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class TryFinallyHook(ast.stmt):
|
|
71
|
+
def __init__(self, finally_body: list[ast.stmt]) -> None:
|
|
72
|
+
self.finally_body = finally_body
|
|
73
|
+
# List of (handler, builtin_name)
|
|
74
|
+
self.handlers_to_restore: list[tuple[str, str]] = []
|
|
75
|
+
lineinfo(self, finally_body[0] if finally_body else None)
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class TryHandlerBodyHook(ast.stmt):
|
|
79
|
+
def __init__(self, handler_body: list[ast.stmt], tracker_name: str) -> None:
|
|
80
|
+
self.handler_body = handler_body
|
|
81
|
+
self.tracker_name = tracker_name
|
|
82
|
+
lineinfo(self, handler_body[0] if handler_body else None)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
class TryBodyHook(ast.stmt):
|
|
86
|
+
def __init__(self, body: list[ast.stmt]) -> None:
|
|
87
|
+
self.body = body
|
|
88
|
+
lineinfo(self, body[0] if body else None)
|
|
89
|
+
self.trackers: list[str] = []
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class StrictCodeGenBase(CinderCodeGenBase):
|
|
93
|
+
flow_graph = PyFlowGraph
|
|
94
|
+
class_list_name: str = "<classes>"
|
|
95
|
+
|
|
96
|
+
def __init__(
|
|
97
|
+
self,
|
|
98
|
+
parent: CodeGenerator | None,
|
|
99
|
+
node: AST,
|
|
100
|
+
symbols: BaseSymbolVisitor,
|
|
101
|
+
graph: PyFlowGraph,
|
|
102
|
+
flags: int = 0,
|
|
103
|
+
optimization_lvl: int = 0,
|
|
104
|
+
builtins: dict[str, Any] = builtins.__dict__,
|
|
105
|
+
future_flags: int | None = None,
|
|
106
|
+
name: str | None = None,
|
|
107
|
+
) -> None:
|
|
108
|
+
super().__init__(
|
|
109
|
+
parent,
|
|
110
|
+
node,
|
|
111
|
+
symbols,
|
|
112
|
+
graph,
|
|
113
|
+
flags=flags,
|
|
114
|
+
optimization_lvl=optimization_lvl,
|
|
115
|
+
future_flags=future_flags,
|
|
116
|
+
name=name,
|
|
117
|
+
)
|
|
118
|
+
self.has_class: bool = self.has_classDef(node)
|
|
119
|
+
self.made_class_list = False
|
|
120
|
+
self.builtins = builtins
|
|
121
|
+
# record the lineno of the first toplevel statement
|
|
122
|
+
# this will be the lineno for all extra generated code
|
|
123
|
+
# in the beginning of the strict module
|
|
124
|
+
self.first_body_node: ast.stmt | None = None
|
|
125
|
+
if not parent and isinstance(node, ast.Module) and len(node.body) > 0:
|
|
126
|
+
self.first_body_node = node.body[0]
|
|
127
|
+
|
|
128
|
+
if parent and isinstance(parent, StrictCodeGenBase):
|
|
129
|
+
self.feature_extractor: FeatureExtractor = parent.feature_extractor
|
|
130
|
+
else:
|
|
131
|
+
self.feature_extractor = FeatureExtractor(builtins, self.future_flags)
|
|
132
|
+
self.feature_extractor.visit(node)
|
|
133
|
+
|
|
134
|
+
@classmethod
|
|
135
|
+
def make_code_gen(
|
|
136
|
+
cls,
|
|
137
|
+
module_name: str,
|
|
138
|
+
tree: AST,
|
|
139
|
+
filename: str,
|
|
140
|
+
source: str | bytes | ast.Module | ast.Expression | ast.Interactive,
|
|
141
|
+
flags: int,
|
|
142
|
+
optimize: int,
|
|
143
|
+
ast_optimizer_enabled: bool = True,
|
|
144
|
+
builtins: dict[str, Any] = builtins.__dict__,
|
|
145
|
+
) -> StrictCodeGenBase:
|
|
146
|
+
future_flags = find_futures(flags, tree)
|
|
147
|
+
if ast_optimizer_enabled:
|
|
148
|
+
tree = cls.optimize_tree(
|
|
149
|
+
optimize, tree, bool(future_flags & CO_FUTURE_ANNOTATIONS)
|
|
150
|
+
)
|
|
151
|
+
s = cls._SymbolVisitor(future_flags)
|
|
152
|
+
s.visit(tree)
|
|
153
|
+
|
|
154
|
+
graph = cls.flow_graph(module_name, filename, s.scopes[tree])
|
|
155
|
+
code_gen = cls(
|
|
156
|
+
None,
|
|
157
|
+
tree,
|
|
158
|
+
s,
|
|
159
|
+
graph,
|
|
160
|
+
flags=flags,
|
|
161
|
+
optimization_lvl=optimize,
|
|
162
|
+
builtins=builtins,
|
|
163
|
+
future_flags=future_flags,
|
|
164
|
+
)
|
|
165
|
+
code_gen._qual_name = module_name
|
|
166
|
+
code_gen.visit(tree)
|
|
167
|
+
return code_gen
|
|
168
|
+
|
|
169
|
+
def is_functionScope(self) -> bool:
|
|
170
|
+
"""Whether the generator has any parent function scope"""
|
|
171
|
+
scope = self.scope
|
|
172
|
+
while scope is not None:
|
|
173
|
+
if isinstance(scope, FunctionScope):
|
|
174
|
+
return True
|
|
175
|
+
scope = scope.parent
|
|
176
|
+
return False
|
|
177
|
+
|
|
178
|
+
def has_classDef(self, node: AST) -> bool:
|
|
179
|
+
visitor = FindClassDef()
|
|
180
|
+
visitor.check(node)
|
|
181
|
+
return visitor.has_class
|
|
182
|
+
|
|
183
|
+
def emit_load_fixed_methods(self) -> None:
|
|
184
|
+
# load and store <strict-modules>
|
|
185
|
+
self.emit("LOAD_NAME", "<fixed-modules>")
|
|
186
|
+
self.emit("LOAD_CONST", "__strict__")
|
|
187
|
+
self.emit_binary_subscr()
|
|
188
|
+
self.emit_dup()
|
|
189
|
+
self.emit("STORE_GLOBAL", "<strict-modules>")
|
|
190
|
+
self.emit("LOAD_CONST", "freeze_type")
|
|
191
|
+
self.emit_binary_subscr()
|
|
192
|
+
self.emit("STORE_GLOBAL", "<freeze-type>")
|
|
193
|
+
|
|
194
|
+
def strictPreVisitCall(self, node: Call) -> None:
|
|
195
|
+
func = node.func
|
|
196
|
+
if isinstance(func, ast.Name):
|
|
197
|
+
# We don't currently allow aliasing or shadowing exec/eval
|
|
198
|
+
# so this check is currently sufficient.
|
|
199
|
+
if func.id == "exec" or func.id == "eval":
|
|
200
|
+
# exec and eval don't take keyword args, so we only need to
|
|
201
|
+
# inspect the normal arguments
|
|
202
|
+
if len(node.args) < 2:
|
|
203
|
+
# we're implicitly capturing globals, make it explicit so
|
|
204
|
+
# that we get our globals dictionary. exec/eval won't be
|
|
205
|
+
# able to mutate them though. We also need to explicitly
|
|
206
|
+
# call locals() here because the behavior of exec/eval is
|
|
207
|
+
# to use globals as locals if it is explicitly supplied.
|
|
208
|
+
def call_function(line_node: AST, name: str) -> expr:
|
|
209
|
+
load = lineinfo(ast.Name(name, ast.Load()))
|
|
210
|
+
call = lineinfo(ast.Call(load, [], []))
|
|
211
|
+
return call
|
|
212
|
+
|
|
213
|
+
node.args.append(call_function(node.args[0], "globals"))
|
|
214
|
+
node.args.append(call_function(node.args[0], "locals"))
|
|
215
|
+
|
|
216
|
+
def visitCall(self, node: Call) -> None:
|
|
217
|
+
self.strictPreVisitCall(node)
|
|
218
|
+
|
|
219
|
+
super().visitCall(node)
|
|
220
|
+
|
|
221
|
+
def visitForBodyHook(self, node: ForBodyHook) -> None:
|
|
222
|
+
self.visit_list(node.body)
|
|
223
|
+
|
|
224
|
+
def strictPreVisitFor(self, node: ast.For) -> None:
|
|
225
|
+
node.body = [ForBodyHook(node.body, node.target)]
|
|
226
|
+
|
|
227
|
+
def strictPostVisitFor(self, node: ast.For) -> None:
|
|
228
|
+
# And undo to keep any introspection happy
|
|
229
|
+
node.body = cast(ForBodyHook, node.body[0]).body
|
|
230
|
+
|
|
231
|
+
def visitFor(self, node: ast.For) -> None:
|
|
232
|
+
self.strictPreVisitFor(node)
|
|
233
|
+
super().visitFor(node)
|
|
234
|
+
self.strictPostVisitFor(node)
|
|
235
|
+
|
|
236
|
+
def make_function(
|
|
237
|
+
self, name: str, body: list[stmt], location_node: ast.AST | None = None
|
|
238
|
+
) -> None:
|
|
239
|
+
# pyre-fixme[20]: Argument `args` expected.
|
|
240
|
+
func = lineinfo(ast.FunctionDef(), location_node)
|
|
241
|
+
func.name = name
|
|
242
|
+
func.decorator_list = []
|
|
243
|
+
func.returns = None
|
|
244
|
+
func.type_comment = ""
|
|
245
|
+
func.body = body
|
|
246
|
+
# pyre-fixme[20]: Argument `args` expected.
|
|
247
|
+
args = lineinfo(ast.arguments())
|
|
248
|
+
func.args = args
|
|
249
|
+
|
|
250
|
+
args.kwonlyargs = []
|
|
251
|
+
args.kw_defaults = []
|
|
252
|
+
args.defaults = []
|
|
253
|
+
args.args = []
|
|
254
|
+
args.posonlyargs = []
|
|
255
|
+
args.kwarg = None
|
|
256
|
+
args.vararg = None
|
|
257
|
+
|
|
258
|
+
scope = FunctionScope(name, self.scope.module, self.scope.klass)
|
|
259
|
+
scope.parent = self.scope
|
|
260
|
+
self.feature_extractor.scopes[func] = scope
|
|
261
|
+
self.scopes[func] = scope
|
|
262
|
+
|
|
263
|
+
self.visitFunctionDef(func)
|
|
264
|
+
|
|
265
|
+
def emit_append_class_list(self) -> None:
|
|
266
|
+
"""
|
|
267
|
+
Assumes the class to append is TOS1.
|
|
268
|
+
Assumes `<classes>` is at TOS
|
|
269
|
+
Pops that class and append to `<classes>`
|
|
270
|
+
Do not Leave `<classes>` on the stack
|
|
271
|
+
"""
|
|
272
|
+
self.emit_rotate_stack(2)
|
|
273
|
+
self.emit("LIST_APPEND", 1)
|
|
274
|
+
self.emit("POP_TOP")
|
|
275
|
+
|
|
276
|
+
def emit_create_class_list(self) -> None:
|
|
277
|
+
"""create and store an empty list `<classes>`"""
|
|
278
|
+
self.emit("BUILD_LIST", 0)
|
|
279
|
+
op = "STORE_FAST" if self.is_functionScope() else "STORE_GLOBAL"
|
|
280
|
+
self.emit(op, self.class_list_name)
|
|
281
|
+
self.made_class_list = True
|
|
282
|
+
|
|
283
|
+
def emit_freeze_class_list(self) -> None:
|
|
284
|
+
"""
|
|
285
|
+
create a for loop that iterates on all of `<classes>`
|
|
286
|
+
assign the value to `<class>`, and call
|
|
287
|
+
freeze_type on `<class>`
|
|
288
|
+
"""
|
|
289
|
+
|
|
290
|
+
start = self.newBlock()
|
|
291
|
+
anchor = self.newBlock()
|
|
292
|
+
after = self.newBlock()
|
|
293
|
+
body = self.newBlock()
|
|
294
|
+
|
|
295
|
+
self.push_loop(FOR_LOOP, start, after)
|
|
296
|
+
self.emit_load_class_list()
|
|
297
|
+
self.emit("GET_ITER")
|
|
298
|
+
|
|
299
|
+
self.nextBlock(start)
|
|
300
|
+
# for <class> in <classes>
|
|
301
|
+
# we don't actually need to assign to <class>
|
|
302
|
+
self.emit("FOR_ITER", anchor)
|
|
303
|
+
self.nextBlock(body)
|
|
304
|
+
self.emit("LOAD_GLOBAL", "<freeze-type>")
|
|
305
|
+
# argument need to be top most
|
|
306
|
+
self.emit_rotate_stack(2)
|
|
307
|
+
self.emit_call_one_arg()
|
|
308
|
+
# discard the result of call
|
|
309
|
+
self.emit("POP_TOP")
|
|
310
|
+
|
|
311
|
+
self.emitJump(start)
|
|
312
|
+
self.nextBlock(anchor)
|
|
313
|
+
self.emit_end_for()
|
|
314
|
+
self.pop_loop()
|
|
315
|
+
self.nextBlock(after)
|
|
316
|
+
|
|
317
|
+
def emit_load_class_list(self) -> None:
|
|
318
|
+
"""load `<classes>` on top of stack"""
|
|
319
|
+
op = "LOAD_FAST" if self.is_functionScope() else "LOAD_GLOBAL"
|
|
320
|
+
self.emit(op, self.class_list_name)
|
|
321
|
+
|
|
322
|
+
def find_immutability_flag(self, node: ClassDef) -> bool:
|
|
323
|
+
old_size = len(node.decorator_list)
|
|
324
|
+
node.decorator_list = [d for d in node.decorator_list if not is_mutable(d)]
|
|
325
|
+
return old_size == len(node.decorator_list)
|
|
326
|
+
|
|
327
|
+
def register_immutability(self, node: ClassDef, flag: bool) -> None:
|
|
328
|
+
if self.has_class and flag:
|
|
329
|
+
self.emit_dup()
|
|
330
|
+
self.emit_load_class_list()
|
|
331
|
+
self.emit_append_class_list()
|
|
332
|
+
super().register_immutability(node, flag)
|
|
333
|
+
|
|
334
|
+
def processBody(
|
|
335
|
+
self,
|
|
336
|
+
node: ast.FunctionDef | ast.AsyncFunctionDef | ast.Lambda,
|
|
337
|
+
body: list[ast.stmt],
|
|
338
|
+
gen: CodeGenerator,
|
|
339
|
+
) -> None:
|
|
340
|
+
if (
|
|
341
|
+
isinstance(node, (FunctionDef, AsyncFunctionDef))
|
|
342
|
+
and isinstance(gen, StrictCodeGenBase)
|
|
343
|
+
and gen.has_class
|
|
344
|
+
):
|
|
345
|
+
# initialize the <classes> list
|
|
346
|
+
if not gen.made_class_list:
|
|
347
|
+
gen.emit_create_class_list()
|
|
348
|
+
# create a try + finally structure where we freeze all classes
|
|
349
|
+
# in the finally block
|
|
350
|
+
gen.emit_try_finally(
|
|
351
|
+
None,
|
|
352
|
+
lambda: super(StrictCodeGenBase, self).processBody(node, body, gen),
|
|
353
|
+
lambda: gen.emit_freeze_class_list(),
|
|
354
|
+
)
|
|
355
|
+
else:
|
|
356
|
+
super().processBody(node, body, gen)
|
|
357
|
+
|
|
358
|
+
def startModule(self) -> None:
|
|
359
|
+
first_node = self.first_body_node
|
|
360
|
+
if first_node:
|
|
361
|
+
self.set_pos(first_node)
|
|
362
|
+
self.emit_load_fixed_methods()
|
|
363
|
+
if self.has_class and not self.made_class_list:
|
|
364
|
+
self.emit_create_class_list()
|
|
365
|
+
super().startModule()
|
|
366
|
+
|
|
367
|
+
def emit_module_return(self, node: ast.Module) -> None:
|
|
368
|
+
if self.has_class:
|
|
369
|
+
self.emit_freeze_class_list()
|
|
370
|
+
super().emit_module_return(node)
|
|
371
|
+
|
|
372
|
+
def emit_import_fixed_modules(
|
|
373
|
+
self, node: ImportFrom, mod_name: str, mod: Mapping[str, object]
|
|
374
|
+
) -> None:
|
|
375
|
+
new_names = [n for n in node.names if n.name not in mod]
|
|
376
|
+
if new_names:
|
|
377
|
+
# We have names we don't know about, keep them around...
|
|
378
|
+
new_node = ImportFrom(mod_name, new_names, node.level)
|
|
379
|
+
super().visitImportFrom(new_node)
|
|
380
|
+
|
|
381
|
+
# Load the module into TOS...
|
|
382
|
+
self.emit("LOAD_NAME", "<fixed-modules>")
|
|
383
|
+
self.emit("LOAD_CONST", mod_name)
|
|
384
|
+
self.emit_binary_subscr() # TOS = mod
|
|
385
|
+
|
|
386
|
+
# Store all of the imported names from the module
|
|
387
|
+
for _i, name in enumerate(node.names):
|
|
388
|
+
var_name = name.name
|
|
389
|
+
asname = name.asname or var_name
|
|
390
|
+
value = mod.get(var_name)
|
|
391
|
+
if value is not None:
|
|
392
|
+
# duplicate TOS (mod)
|
|
393
|
+
self.emit_dup()
|
|
394
|
+
# var name
|
|
395
|
+
self.emit("LOAD_CONST", var_name)
|
|
396
|
+
self.emit_binary_subscr()
|
|
397
|
+
self.emit("STORE_GLOBAL", asname)
|
|
398
|
+
# remove TOS (mod)
|
|
399
|
+
self.emit("POP_TOP")
|
|
400
|
+
|
|
401
|
+
@final
|
|
402
|
+
def visitImportFrom(self, node: ImportFrom) -> None:
|
|
403
|
+
if node.level == 0 and node.module is not None and node.module in FIXED_MODULES:
|
|
404
|
+
module_name = node.module
|
|
405
|
+
assert module_name is not None
|
|
406
|
+
mod = FIXED_MODULES[module_name]
|
|
407
|
+
self.emit_import_fixed_modules(node, module_name, mod)
|
|
408
|
+
return
|
|
409
|
+
super().visitImportFrom(node)
|