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.
Files changed (68) hide show
  1. __static__/__init__.py +641 -0
  2. __static__/compiler_flags.py +8 -0
  3. __static__/enum.py +160 -0
  4. __static__/native_utils.py +77 -0
  5. __static__/type_code.py +48 -0
  6. __strict__/__init__.py +39 -0
  7. _cinderx.so +0 -0
  8. cinderx/__init__.py +577 -0
  9. cinderx/__pycache__/__init__.cpython-314.pyc +0 -0
  10. cinderx/_asyncio.py +156 -0
  11. cinderx/compileall.py +710 -0
  12. cinderx/compiler/__init__.py +40 -0
  13. cinderx/compiler/__main__.py +137 -0
  14. cinderx/compiler/config.py +7 -0
  15. cinderx/compiler/consts.py +72 -0
  16. cinderx/compiler/debug.py +70 -0
  17. cinderx/compiler/dis_stable.py +283 -0
  18. cinderx/compiler/errors.py +151 -0
  19. cinderx/compiler/flow_graph_optimizer.py +1287 -0
  20. cinderx/compiler/future.py +91 -0
  21. cinderx/compiler/misc.py +32 -0
  22. cinderx/compiler/opcode_cinder.py +18 -0
  23. cinderx/compiler/opcode_static.py +100 -0
  24. cinderx/compiler/opcodebase.py +158 -0
  25. cinderx/compiler/opcodes.py +991 -0
  26. cinderx/compiler/optimizer.py +547 -0
  27. cinderx/compiler/pyassem.py +3711 -0
  28. cinderx/compiler/pycodegen.py +7660 -0
  29. cinderx/compiler/pysourceloader.py +62 -0
  30. cinderx/compiler/static/__init__.py +1404 -0
  31. cinderx/compiler/static/compiler.py +629 -0
  32. cinderx/compiler/static/declaration_visitor.py +335 -0
  33. cinderx/compiler/static/definite_assignment_checker.py +280 -0
  34. cinderx/compiler/static/effects.py +160 -0
  35. cinderx/compiler/static/module_table.py +666 -0
  36. cinderx/compiler/static/type_binder.py +2176 -0
  37. cinderx/compiler/static/types.py +10580 -0
  38. cinderx/compiler/static/util.py +81 -0
  39. cinderx/compiler/static/visitor.py +91 -0
  40. cinderx/compiler/strict/__init__.py +69 -0
  41. cinderx/compiler/strict/class_conflict_checker.py +249 -0
  42. cinderx/compiler/strict/code_gen_base.py +409 -0
  43. cinderx/compiler/strict/common.py +507 -0
  44. cinderx/compiler/strict/compiler.py +352 -0
  45. cinderx/compiler/strict/feature_extractor.py +130 -0
  46. cinderx/compiler/strict/flag_extractor.py +97 -0
  47. cinderx/compiler/strict/loader.py +827 -0
  48. cinderx/compiler/strict/preprocessor.py +11 -0
  49. cinderx/compiler/strict/rewriter/__init__.py +5 -0
  50. cinderx/compiler/strict/rewriter/remove_annotations.py +84 -0
  51. cinderx/compiler/strict/rewriter/rewriter.py +975 -0
  52. cinderx/compiler/strict/runtime.py +77 -0
  53. cinderx/compiler/symbols.py +1754 -0
  54. cinderx/compiler/unparse.py +414 -0
  55. cinderx/compiler/visitor.py +194 -0
  56. cinderx/jit.py +230 -0
  57. cinderx/opcode.py +202 -0
  58. cinderx/static.py +113 -0
  59. cinderx/strictmodule.py +6 -0
  60. cinderx/test_support.py +341 -0
  61. cinderx-2026.1.16.2.dist-info/METADATA +15 -0
  62. cinderx-2026.1.16.2.dist-info/RECORD +68 -0
  63. cinderx-2026.1.16.2.dist-info/WHEEL +6 -0
  64. cinderx-2026.1.16.2.dist-info/licenses/LICENSE +21 -0
  65. cinderx-2026.1.16.2.dist-info/top_level.txt +5 -0
  66. opcodes/__init__.py +0 -0
  67. opcodes/assign_opcode_numbers.py +272 -0
  68. opcodes/cinderx_opcodes.py +121 -0
@@ -0,0 +1,507 @@
1
+ # Copyright (c) Meta Platforms, Inc. and affiliates.
2
+ # pyre-strict
3
+
4
+ from __future__ import annotations
5
+
6
+ import ast
7
+ import os.path
8
+ import symtable
9
+ import sys
10
+ import typing
11
+ from ast import (
12
+ alias,
13
+ AST,
14
+ AsyncFunctionDef,
15
+ ClassDef,
16
+ comprehension,
17
+ copy_location,
18
+ DictComp,
19
+ FunctionDef,
20
+ GeneratorExp,
21
+ iter_fields,
22
+ Lambda,
23
+ ListComp,
24
+ NodeVisitor,
25
+ SetComp,
26
+ Try,
27
+ )
28
+ from collections import deque
29
+ from collections.abc import Callable, Mapping, MutableMapping
30
+ from contextlib import nullcontext
31
+ from dataclasses import dataclass
32
+ from symtable import Class, SymbolTable
33
+ from typing import final, Generic, TypeVar
34
+
35
+ from .runtime import freeze_type, mutable
36
+
37
+
38
+ # Increment this whenever we change the output of the strict modules
39
+ # interpreter. It must stay below 32768 (15 bits), because we use the high bit
40
+ # to encode strictness of the module.
41
+ MAGIC_NUMBER = 55
42
+
43
+
44
+ DEFAULT_STUB_PATH: str = os.path.dirname(__file__) + "/stubs"
45
+
46
+
47
+ def make_fixed_modules() -> Mapping[str, Mapping[str, object]]:
48
+ typing_members = {}
49
+ for name in typing.__all__:
50
+ typing_members[name] = getattr(typing, name)
51
+ strict_mod_members = {
52
+ "freeze_type": freeze_type,
53
+ "mutable": mutable,
54
+ }
55
+
56
+ return {
57
+ "typing": typing_members,
58
+ "strict_modules": dict(strict_mod_members),
59
+ "__strict__": strict_mod_members,
60
+ }
61
+
62
+
63
+ FIXED_MODULES: Mapping[str, Mapping[str, object]] = make_fixed_modules()
64
+
65
+
66
+ TVar = TypeVar("TScope")
67
+ TScopeData = TypeVar("TData", covariant=True)
68
+
69
+ SymbolMap = dict[AST, SymbolTable]
70
+
71
+
72
+ @dataclass
73
+ class StrictModuleError(Exception):
74
+ msg: str
75
+ filename: str
76
+ lineno: int
77
+ col: int
78
+ metadata: str = ""
79
+
80
+
81
+ def _is_scoped_generator_node(node: AST) -> bool:
82
+ return sys.version_info < (3, 12) or not isinstance(
83
+ node,
84
+ (
85
+ ast.ListComp,
86
+ ast.DictComp,
87
+ ast.SetComp,
88
+ ),
89
+ )
90
+
91
+
92
+ # SymbolTableType is new in 3.13
93
+ if sys.version_info >= (3, 14):
94
+ ANNOTATION_TYPE: str = symtable.SymbolTableType.ANNOTATION
95
+
96
+ def is_annotation(x: symtable.SymbolTable) -> bool:
97
+ return x.get_type() == ANNOTATION_TYPE
98
+ else:
99
+
100
+ def is_annotation(x: symtable.SymbolTable) -> bool:
101
+ return False
102
+
103
+
104
+ @final
105
+ class SymbolMapBuilder(ast.NodeVisitor):
106
+ def __init__(self, symbols: SymbolTable) -> None:
107
+ self.symbol_stack: deque[SymbolTable] = deque([symbols])
108
+ children = self.symbol_stack.popleft().get_children()
109
+ self.symbol_stack.extendleft(
110
+ x for x in reversed(children) if not is_annotation(x)
111
+ )
112
+ self.mapping: SymbolMap = {}
113
+
114
+ def _process_scope_node(self, node: AST) -> None:
115
+ current_symbol = self.mapping[node] = self.symbol_stack.popleft()
116
+ children = current_symbol.get_children()
117
+ self.symbol_stack.extendleft(
118
+ x for x in reversed(children) if not is_annotation(x)
119
+ )
120
+
121
+ def visit_ClassDef(self, node: ClassDef) -> None:
122
+ for child in node.bases:
123
+ self.visit(child)
124
+ for child in node.keywords:
125
+ self.visit(child)
126
+ for child in node.decorator_list:
127
+ self.visit(child)
128
+ self._process_scope_node(node)
129
+ for child in node.body:
130
+ self.visit(child)
131
+
132
+ def visit_FunctionDef(self, node: FunctionDef) -> None:
133
+ self._visit_function_like(node)
134
+
135
+ def visit_AsyncFunctionDef(self, node: AsyncFunctionDef) -> None:
136
+ self._visit_function_like(node)
137
+
138
+ def visit_Lambda(self, node: Lambda) -> None:
139
+ if node.args:
140
+ self.visit(node.args)
141
+ self._process_scope_node(node)
142
+ self.visit(node.body)
143
+
144
+ def _visit_function_like(self, node: FunctionDef | AsyncFunctionDef) -> None:
145
+ # args -> returns -> decorator_list -> body
146
+ if node.args:
147
+ self.visit(node.args)
148
+ returns = node.returns
149
+ if returns:
150
+ self.visit(returns)
151
+ for child in node.decorator_list:
152
+ self.visit(child)
153
+ self._process_scope_node(node)
154
+ for child in node.body:
155
+ self.visit(child)
156
+
157
+ def visit_ListComp(self, node: ListComp) -> None:
158
+ self._visit_generator_like(node, [node.elt], node.generators)
159
+
160
+ def visit_SetComp(self, node: SetComp) -> None:
161
+ self._visit_generator_like(node, [node.elt], node.generators)
162
+
163
+ def visit_DictComp(self, node: DictComp) -> None:
164
+ self._visit_generator_like(node, [node.key, node.value], node.generators)
165
+
166
+ def visit_GeneratorExp(self, node: GeneratorExp) -> None:
167
+ self._visit_generator_like(node, [node.elt], node.generators)
168
+
169
+ def _visit_generator_like(
170
+ self, node: AST, elements: list[AST], comprehensions: list[comprehension]
171
+ ) -> None:
172
+ # first iter is visited in the outer scope
173
+ self.visit(comprehensions[0].iter)
174
+ # everything else is in the inner scope
175
+ if _is_scoped_generator_node(node):
176
+ # In 3.12 list comprehensions are inlined
177
+ self._process_scope_node(node)
178
+ # process first comprehension, without iter
179
+ for child in comprehensions[0].ifs:
180
+ self.visit(child)
181
+ for comp in comprehensions[1:]:
182
+ self.visit(comp)
183
+ # process elements
184
+ for element in elements:
185
+ self.visit(element)
186
+
187
+ def visit_Try(self, node: Try) -> None:
188
+ # Need to match the order the symbol visitor constructs these in, which
189
+ # walks orelse before handlers.
190
+ for val in node.body:
191
+ self.visit(val)
192
+ for val in node.orelse:
193
+ self.visit(val)
194
+ for val in node.handlers:
195
+ self.visit(val)
196
+ for val in node.finalbody:
197
+ self.visit(val)
198
+
199
+
200
+ def get_symbol_map(node: ast.AST, symtable: SymbolTable) -> SymbolMap:
201
+ visitor = SymbolMapBuilder(symtable)
202
+ visitor.visit(node)
203
+ return visitor.mapping
204
+
205
+
206
+ @final
207
+ class SymbolScope(Generic[TVar, TScopeData]):
208
+ def __init__(
209
+ self,
210
+ symbols: SymbolTable,
211
+ scope_data: TScopeData,
212
+ vars: MutableMapping[str, TVar] | None = None,
213
+ invisible: bool = False,
214
+ ) -> None:
215
+ self.symbols = symbols
216
+ self.vars = vars
217
+ self.scope_data = scope_data
218
+ self.invisible = invisible
219
+
220
+ def __getitem__(self, name: str) -> TVar:
221
+ v = self.vars
222
+ if v is None:
223
+ raise KeyError(name)
224
+
225
+ return v[name]
226
+
227
+ def __setitem__(self, name: str, value: TVar) -> None:
228
+ v = self.vars
229
+ if v is None:
230
+ v = self.vars = {}
231
+
232
+ v[name] = value
233
+
234
+ def __delitem__(self, name: str) -> None:
235
+ v = self.vars
236
+ if v is None:
237
+ raise KeyError(name)
238
+
239
+ del v[name]
240
+
241
+ def __contains__(self, name: str) -> bool:
242
+ v = self.vars
243
+ if v is None:
244
+ return False
245
+
246
+ return name in v
247
+
248
+
249
+ def mangle_priv_name(name: str, scopes: list[SymbolScope[TVar, TScopeData]]) -> str:
250
+ if name.startswith("__") and not name.endswith("__"):
251
+ # symtable has name mangled private names. Walk the scope list
252
+ # backwards and apply the mangled class name
253
+ for scope in reversed(scopes):
254
+ if isinstance(scope.symbols, symtable.Class) and not scope.invisible:
255
+ return "_" + scope.symbols.get_name().lstrip("_") + name
256
+
257
+ return name
258
+
259
+
260
+ def imported_name(name: alias) -> str:
261
+ return name.asname or name.name.partition(".")[0]
262
+
263
+
264
+ @final
265
+ class ScopeContextManager(Generic[TVar, TScopeData]):
266
+ def __init__(
267
+ self, parent: ScopeStack[TVar, TScopeData], scope: SymbolScope[TVar, TScopeData]
268
+ ) -> None:
269
+ self.parent = parent
270
+ self.scope = scope
271
+
272
+ def __enter__(self) -> SymbolScope[TVar, TScopeData]:
273
+ self.parent.push(self.scope)
274
+ return self.scope
275
+
276
+ def __exit__(
277
+ self, exc_type: type[Exception], exc_val: Exception, exc_tb: object
278
+ ) -> None:
279
+ self.parent.pop()
280
+
281
+
282
+ @final
283
+ class ScopeStack(Generic[TVar, TScopeData]):
284
+ def __init__(
285
+ self,
286
+ *scopes: SymbolScope[TVar, TScopeData],
287
+ symbol_map: SymbolMap,
288
+ scope_factory: Callable[
289
+ [SymbolTable, AST, MutableMapping[str, TVar] | None],
290
+ SymbolScope[TVar, TScopeData],
291
+ ] = lambda symtable, node, vars: SymbolScope(symtable, None),
292
+ ) -> None:
293
+ self.scopes: list[SymbolScope[TVar, TScopeData]] = list(scopes)
294
+ self.symbol_map = symbol_map
295
+ self.scope_factory: Callable[
296
+ [SymbolTable, AST, MutableMapping[str, TVar] | None],
297
+ SymbolScope[TVar, TScopeData],
298
+ ] = scope_factory
299
+
300
+ def push(self, scope: SymbolScope[TVar, TScopeData]) -> None:
301
+ self.scopes.append(scope)
302
+
303
+ def pop(self) -> None:
304
+ self.scopes.pop()
305
+
306
+ @property
307
+ def current_symbols(self) -> SymbolTable:
308
+ return self.scopes[-1].symbols
309
+
310
+ @property
311
+ def current(self) -> SymbolScope[TVar, TScopeData]:
312
+ return self.scopes[-1]
313
+
314
+ @property
315
+ def in_global_scope(self) -> bool:
316
+ return len(self.scopes) == 1
317
+
318
+ @property
319
+ def in_class_scope(self) -> bool:
320
+ return isinstance(self.scopes[-1].symbols, Class)
321
+
322
+ def with_node_scope(
323
+ self, node: AST, vars: MutableMapping[str, TVar] | None = None
324
+ ) -> ScopeContextManager[TVar, TScopeData] | nullcontext[None]:
325
+ if not _is_scoped_generator_node(node):
326
+ assert isinstance(node, (ast.ListComp, ast.DictComp, ast.SetComp))
327
+ # In 3.12 list/dict/set comprehensions are inlined
328
+ return nullcontext()
329
+
330
+ next_symtable = self.symbol_map[node]
331
+ return ScopeContextManager(self, self.scope_factory(next_symtable, node, vars))
332
+
333
+ def is_global(self, name: str) -> bool:
334
+ if self.in_global_scope:
335
+ return True
336
+
337
+ return self.current_symbols.lookup(
338
+ mangle_priv_name(name, self.scopes)
339
+ ).is_global()
340
+
341
+ def is_nonlocal(self, name: str) -> bool:
342
+ return (
343
+ not self.is_global(name)
344
+ and self.current_symbols.lookup(
345
+ mangle_priv_name(name, self.scopes)
346
+ ).is_free()
347
+ )
348
+
349
+ def is_local(self, name: str) -> bool:
350
+ return self.current_symbols.lookup(
351
+ mangle_priv_name(name, self.scopes)
352
+ ).is_local()
353
+
354
+ def scope_for(self, name: str) -> SymbolScope[TVar, TScopeData]:
355
+ if self.is_global(name):
356
+ return self.scopes[0]
357
+ elif self.is_nonlocal(name):
358
+ for scope in reversed(self.scopes):
359
+ lookup = scope.symbols.lookup(name)
360
+ if lookup.is_global() or lookup.is_free():
361
+ return scope
362
+
363
+ return self.scopes[-1]
364
+
365
+ def __getitem__(self, name: str) -> TVar:
366
+ is_leaf = True
367
+ for scope in reversed(self.scopes):
368
+ if (is_leaf or not isinstance(scope.symbols, Class)) and name in scope:
369
+ return scope[name]
370
+ is_leaf = False
371
+ raise KeyError(f"{name} not found in scope")
372
+
373
+ def __contains__(self, name: str) -> bool:
374
+ is_leaf = True
375
+ for scope in reversed(self.scopes):
376
+ if (is_leaf or not isinstance(scope.symbols, Class)) and name in scope:
377
+ return True
378
+ is_leaf = False
379
+ return False
380
+
381
+ def __setitem__(self, name: str, value: TVar) -> None:
382
+ if self.is_global(name):
383
+ self.scopes[0][name] = value
384
+ elif self.is_nonlocal(name):
385
+ for scope in reversed(self.scopes[:-1]):
386
+ if name in scope:
387
+ scope[name] = value
388
+ break
389
+ else:
390
+ current_scope = self.scopes[-1]
391
+ current_scope[name] = value
392
+
393
+ def __delitem__(self, name: str) -> None:
394
+ if self.is_global(name):
395
+ if name in self.scopes[0]:
396
+ del self.scopes[0][name]
397
+ elif self.is_nonlocal(name):
398
+ for scope in reversed(self.scopes[:-1]):
399
+ if name in scope:
400
+ del scope[name]
401
+ break
402
+ else:
403
+ current_scope = self.scopes[-1]
404
+ if name in current_scope:
405
+ del current_scope[name]
406
+
407
+ def local_contains(self, name: str) -> bool:
408
+ return name in self.scopes[-1]
409
+
410
+ def shallow_copy(self) -> ScopeStack[TVar, TScopeData]:
411
+ # same underlying stack but a different dict
412
+ return ScopeStack(
413
+ *(d for d in self.scopes),
414
+ symbol_map=self.symbol_map,
415
+ scope_factory=self.scope_factory,
416
+ )
417
+
418
+ def with_scope(
419
+ self, scope: SymbolScope[TVar, TScopeData]
420
+ ) -> ScopeContextManager[TVar, TScopeData]:
421
+ return ScopeContextManager(self, scope)
422
+
423
+
424
+ TAst = TypeVar("TAst", bound=AST)
425
+
426
+
427
+ class AstRewriter(NodeVisitor):
428
+ """performs rewrites on the AST, but only produces new nodes, rather than
429
+ modifying nodes in place like ast.NodeTransformer."""
430
+
431
+ @staticmethod
432
+ def update_node(node: TAst, **replacement: object) -> TAst:
433
+ res = node
434
+ for name, val in replacement.items():
435
+ existing = getattr(res, name)
436
+ if existing == val:
437
+ continue
438
+
439
+ if node is res:
440
+ res = AstRewriter.clone_node(node)
441
+
442
+ setattr(res, name, val)
443
+ return res
444
+
445
+ @staticmethod
446
+ def clone_node(node: TAst) -> TAst:
447
+ attrs = []
448
+ for name in node._fields:
449
+ attr = getattr(node, name, None)
450
+ if isinstance(attr, list):
451
+ attr = list(attr)
452
+ attrs.append(attr)
453
+
454
+ new = type(node)(*attrs)
455
+ return copy_location(new, node)
456
+
457
+ def walk_list(self, old_values: list[TAst]) -> list[TAst]:
458
+ new_values = []
459
+ changed = False
460
+ for old_value in old_values:
461
+ if isinstance(old_value, AST):
462
+ new_value = self.visit(old_value)
463
+ changed |= new_value is not old_value
464
+ if new_value is None:
465
+ continue
466
+ elif not isinstance(new_value, AST):
467
+ new_values.extend(new_value)
468
+ continue
469
+
470
+ new_values.append(new_value)
471
+ else:
472
+ new_values.append(old_value)
473
+ return new_values if changed else old_values
474
+
475
+ def generic_visit(self, node: TAst) -> TAst:
476
+ ret_node = node
477
+ for field, old_value in iter_fields(node):
478
+ if isinstance(old_value, list):
479
+ new_values = self.walk_list(old_value)
480
+ if new_values != old_value:
481
+ if ret_node is node:
482
+ ret_node = self.clone_node(node)
483
+ setattr(ret_node, field, new_values)
484
+ elif isinstance(old_value, AST):
485
+ new_node = self.visit(old_value)
486
+ assert new_node is not None, (
487
+ "can't remove AST nodes that aren't part of a list"
488
+ )
489
+ if new_node is not old_value:
490
+ if ret_node is node:
491
+ ret_node = self.clone_node(node)
492
+
493
+ setattr(ret_node, field, new_node)
494
+
495
+ return ret_node
496
+
497
+
498
+ def lineinfo(node: TAst, target: AST | None = None) -> TAst:
499
+ if not target:
500
+ # set lineno to -1 to indicate non-user code
501
+ node.lineno = -1
502
+ node.col_offset = -1
503
+ node.end_lineno = -1
504
+ node.end_col_offset = -1
505
+ else:
506
+ copy_location(node, target)
507
+ return node