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,81 @@
1
+ # Copyright (c) Meta Platforms, Inc. and affiliates.
2
+ # pyre-strict
3
+
4
+ from __future__ import annotations
5
+
6
+ import ast
7
+ import sys
8
+ from typing import Callable, Mapping
9
+
10
+ COMPARE_OPS: Mapping[type[ast.cmpop], Callable[[object, object], bool]] = {
11
+ ast.Gt: lambda a, b: a > b,
12
+ ast.GtE: lambda a, b: a >= b,
13
+ ast.Lt: lambda a, b: a < b,
14
+ ast.LtE: lambda a, b: a <= b,
15
+ ast.Eq: lambda a, b: a == b,
16
+ ast.NotEq: lambda a, b: a != b,
17
+ ast.In: lambda a, b: a in b,
18
+ ast.Is: lambda a, b: a is b,
19
+ ast.IsNot: lambda a, b: a is not b,
20
+ }
21
+
22
+
23
+ def make_qualname(parent_qualname: str | None, name: str) -> str:
24
+ if parent_qualname is None:
25
+ return name
26
+ return f"{parent_qualname}.{name}"
27
+
28
+
29
+ def _is_sys_hexversion_attr_load(node: ast.expr) -> bool:
30
+ if isinstance(node, ast.Attribute):
31
+ container = node.value
32
+ if (
33
+ isinstance(container, ast.Name)
34
+ and container.id == "sys"
35
+ and isinstance(node.ctx, ast.Load)
36
+ and node.attr == "hexversion"
37
+ ):
38
+ return True
39
+ return False
40
+
41
+
42
+ def _get_const_int(node: ast.expr) -> int | None:
43
+ if isinstance(node, ast.Constant):
44
+ value = node.value
45
+ return value if isinstance(value, int) else None
46
+
47
+
48
+ def sys_hexversion_check(
49
+ node: ast.If,
50
+ ) -> bool | None:
51
+ """
52
+ A helper function, the result of this is used to determine whether
53
+ we need to skip visiting dead code gated by sys.hexversion checks.
54
+ """
55
+ test_node = node.test
56
+ if isinstance(test_node, ast.Compare):
57
+ if len(test_node.comparators) != 1:
58
+ return None
59
+
60
+ assert len(test_node.ops) == 1
61
+
62
+ left = test_node.left
63
+ right = test_node.comparators[0]
64
+ op = test_node.ops[0]
65
+
66
+ if type(op) in (ast.In, ast.Is, ast.IsNot):
67
+ return None
68
+
69
+ if _is_sys_hexversion_attr_load(left):
70
+ left_value = sys.hexversion
71
+ right_value = _get_const_int(right)
72
+ elif _is_sys_hexversion_attr_load(right):
73
+ left_value = _get_const_int(left)
74
+ right_value = sys.hexversion
75
+ else:
76
+ return None
77
+
78
+ if left_value is None or right_value is None:
79
+ return None
80
+
81
+ return COMPARE_OPS[type(op)](left_value, right_value)
@@ -0,0 +1,91 @@
1
+ # Copyright (c) Meta Platforms, Inc. and affiliates.
2
+ # pyre-strict
3
+
4
+ from __future__ import annotations
5
+
6
+ from ast import AST
7
+ from contextlib import contextmanager, nullcontext
8
+ from typing import ContextManager, Generator, Generic, Sequence, TYPE_CHECKING, TypeVar
9
+
10
+ from ..visitor import ASTVisitor
11
+
12
+ if TYPE_CHECKING:
13
+ from ..errors import ErrorSink
14
+ from .compiler import Compiler
15
+ from .module_table import ModuleTable
16
+ from .types import TypeEnvironment
17
+
18
+
19
+ TVisitRet = TypeVar("TVisitRet", covariant=True)
20
+
21
+
22
+ class GenericVisitor(ASTVisitor, Generic[TVisitRet]):
23
+ def __init__(self, module: ModuleTable) -> None:
24
+ super().__init__()
25
+ self.module = module
26
+ self.module_name: str = module.name
27
+ self.filename: str = module.filename
28
+ self.compiler: Compiler = module.compiler
29
+ self.error_sink: ErrorSink = module.compiler.error_sink
30
+ self.type_env: TypeEnvironment = module.compiler.type_env
31
+ # the qualname that should be the "requester" of types used (for dep tracking)
32
+ self._context_qualname: str = ""
33
+ # if true, all deps tracked in visiting should be considered decl deps
34
+ self.force_decl_deps: bool = False
35
+
36
+ @property
37
+ def context_qualname(self) -> str:
38
+ return self._context_qualname
39
+
40
+ @contextmanager
41
+ def temporary_context_qualname(
42
+ self, qualname: str | None, force_decl: bool = False
43
+ ) -> Generator[None, None, None]:
44
+ old_qualname = self._context_qualname
45
+ self._context_qualname = qualname or ""
46
+ old_decl = self.force_decl_deps
47
+ self.force_decl_deps = force_decl
48
+ try:
49
+ yield
50
+ finally:
51
+ self._context_qualname = old_qualname
52
+ self.force_decl_deps = old_decl
53
+
54
+ def record_dependency(self, source: tuple[str, str]) -> None:
55
+ self.module.record_dependency(
56
+ self.context_qualname, source, force_decl=self.force_decl_deps
57
+ )
58
+
59
+ def visit(self, node: AST, *args: object) -> TVisitRet:
60
+ # if we have a sequence of nodes, don't catch TypedSyntaxError here;
61
+ # walk_list will call us back with each individual node in turn and we
62
+ # can catch errors and add node info then.
63
+ ctx = self.error_context(node) if isinstance(node, AST) else nullcontext()
64
+ with ctx:
65
+ if not args:
66
+ # pyre-ignore: ASTVisitor is not generic yet, can't assert the result is
67
+ # TVisitRet.
68
+ return super().visit(node)
69
+ # pyre-ignore: ASTVisitor is not generic yet, can't assert the result is
70
+ # TVisitRet.
71
+ return super().visit(node, *args)
72
+
73
+ def syntax_error(self, msg: str, node: AST) -> None:
74
+ return self.error_sink.syntax_error(msg, self.filename, node)
75
+
76
+ def perf_warning(self, msg: str, node: AST) -> None:
77
+ return self.error_sink.perf_warning(msg, self.filename, node)
78
+
79
+ def error_context(self, node: AST | None) -> ContextManager[None]:
80
+ if node is None:
81
+ return nullcontext()
82
+ return self.error_sink.error_context(self.filename, node)
83
+
84
+ @contextmanager
85
+ def temporary_error_sink(self, sink: ErrorSink) -> Generator[None, None, None]:
86
+ orig_sink = self.error_sink
87
+ self.error_sink = sink
88
+ try:
89
+ yield
90
+ finally:
91
+ self.error_sink = orig_sink
@@ -0,0 +1,69 @@
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 types import CodeType
11
+ from typing import Any
12
+
13
+ from ..pyassem import (
14
+ PyFlowGraph312,
15
+ PyFlowGraph314,
16
+ PyFlowGraph315,
17
+ PyFlowGraphCinder310,
18
+ )
19
+ from ..pycodegen import (
20
+ CinderCodeGenerator310,
21
+ CinderCodeGenerator312,
22
+ CinderCodeGenerator314,
23
+ CinderCodeGenerator315,
24
+ )
25
+ from .code_gen_base import StrictCodeGenBase
26
+
27
+
28
+ # Unused but still present until we remove it from IGSRV
29
+ enable_strict_features: bool = True
30
+
31
+
32
+ class StrictCodeGenerator310(StrictCodeGenBase, CinderCodeGenerator310):
33
+ flow_graph = PyFlowGraphCinder310
34
+
35
+
36
+ class StrictCodeGenerator312(StrictCodeGenBase, CinderCodeGenerator312):
37
+ flow_graph = PyFlowGraph312
38
+
39
+
40
+ class StrictCodeGenerator314(StrictCodeGenerator312, CinderCodeGenerator314):
41
+ flow_graph = PyFlowGraph314
42
+
43
+
44
+ class StrictCodeGenerator315(StrictCodeGenerator312, CinderCodeGenerator315):
45
+ flow_graph = PyFlowGraph315
46
+
47
+
48
+ def strict_compile(
49
+ name: str,
50
+ filename: str,
51
+ tree: ast.Module,
52
+ source: str | bytes,
53
+ optimize: int = 0,
54
+ builtins: dict[str, Any] = builtins.__dict__,
55
+ ) -> CodeType:
56
+ code_gen = StrictCodeGenerator.make_code_gen(
57
+ name, tree, filename, source, flags=0, optimize=optimize, builtins=builtins
58
+ )
59
+ return code_gen.getCode()
60
+
61
+
62
+ if sys.version_info >= (3, 15):
63
+ StrictCodeGenerator = StrictCodeGenerator315
64
+ elif sys.version_info >= (3, 14):
65
+ StrictCodeGenerator = StrictCodeGenerator314
66
+ elif sys.version_info >= (3, 12):
67
+ StrictCodeGenerator = StrictCodeGenerator312
68
+ else:
69
+ StrictCodeGenerator = StrictCodeGenerator310
@@ -0,0 +1,249 @@
1
+ # Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ # pyre-strict
4
+
5
+ from __future__ import annotations
6
+
7
+ import ast
8
+ from ast import (
9
+ AnnAssign,
10
+ Assign,
11
+ AST,
12
+ AsyncFunctionDef,
13
+ Attribute,
14
+ ClassDef,
15
+ Delete,
16
+ ExceptHandler,
17
+ FunctionDef,
18
+ Global,
19
+ Import,
20
+ ImportFrom,
21
+ Module,
22
+ Name,
23
+ )
24
+ from symtable import SymbolTable
25
+ from typing import final, MutableMapping
26
+
27
+ from ..consts import CO_FUTURE_ANNOTATIONS
28
+ from ..pycodegen import find_futures
29
+ from .common import (
30
+ get_symbol_map,
31
+ imported_name,
32
+ ScopeStack,
33
+ StrictModuleError,
34
+ SymbolMap,
35
+ SymbolScope,
36
+ )
37
+ from .rewriter.rewriter import SymbolVisitor
38
+
39
+ CLASS_ATTR_CONFLICT_EXCEPTION = "ClassAttributesConflictException"
40
+
41
+
42
+ class TransformerScope:
43
+ def visit_Assign(self, node: Assign) -> None:
44
+ pass
45
+
46
+ def visit_AnnAssign(self, node: AnnAssign) -> None:
47
+ pass
48
+
49
+ def loaded(self, name: str) -> None:
50
+ pass
51
+
52
+ def stored(self, name: str) -> None:
53
+ pass
54
+
55
+
56
+ @final
57
+ class ClassScope(TransformerScope):
58
+ def __init__(self) -> None:
59
+ self.instance_fields: set[str] = set()
60
+ self.class_fields: set[str] = set()
61
+
62
+ def visit_AnnAssign(self, node: AnnAssign) -> None:
63
+ target = node.target
64
+ if isinstance(target, Name):
65
+ if node.value is None:
66
+ self.instance_fields.add(target.id)
67
+ else:
68
+ self.class_fields.add(target.id)
69
+
70
+ def stored(self, name: str) -> None:
71
+ self.class_fields.add(name)
72
+
73
+
74
+ @final
75
+ class FunctionScope(TransformerScope):
76
+ def __init__(self, node: FunctionDef, parent: TransformerScope) -> None:
77
+ self.node = node
78
+ self.parent = parent
79
+
80
+ def visit_AnnAssign(self, node: AnnAssign) -> None:
81
+ parent = self.parent
82
+ target = node.target
83
+ if (
84
+ isinstance(self.node, FunctionDef)
85
+ and self.node.name == "__init__"
86
+ and self.node.args.args
87
+ and isinstance(parent, ClassScope)
88
+ and isinstance(target, Attribute)
89
+ ):
90
+ self.add_attr_name(target, parent)
91
+
92
+ def add_attr_name(self, target: Attribute, parent: ClassScope) -> None:
93
+ """records self.name = ... when salf matches the 1st parameter"""
94
+ value = target.value
95
+ node = self.node
96
+ if (
97
+ isinstance(node, FunctionDef)
98
+ and isinstance(value, Name)
99
+ and value.id == node.args.args[0].arg
100
+ ):
101
+ parent.instance_fields.add(target.attr)
102
+
103
+ def visit_Assign(self, node: Assign) -> None:
104
+ parent = self.parent
105
+ if (
106
+ isinstance(self.node, FunctionDef)
107
+ and self.node.name == "__init__"
108
+ and isinstance(parent, ClassScope)
109
+ and self.node.args.args
110
+ ):
111
+ for target in node.targets:
112
+ if not isinstance(target, Attribute):
113
+ continue
114
+ self.add_attr_name(target, parent)
115
+
116
+
117
+ @final
118
+ class ClassConflictChecker(SymbolVisitor[object, TransformerScope]):
119
+ def __init__(
120
+ self,
121
+ symbols: SymbolTable,
122
+ symbol_map: SymbolMap,
123
+ filename: str,
124
+ flags: int,
125
+ ) -> None:
126
+ super().__init__(
127
+ ScopeStack(
128
+ self.make_scope(symbols, None),
129
+ symbol_map=symbol_map,
130
+ scope_factory=self.make_scope,
131
+ ),
132
+ )
133
+ self.filename = filename
134
+ self.flags = flags
135
+
136
+ @property
137
+ def skip_annotations(self) -> bool:
138
+ return bool(self.flags & CO_FUTURE_ANNOTATIONS)
139
+
140
+ def error(self, names: list[str], lineno: int, col: int, filename: str) -> None:
141
+ MSG: str = "Class member conflicts with instance member: {names}"
142
+ raise StrictModuleError(MSG.format(names=names), filename, lineno, col)
143
+
144
+ def make_scope(
145
+ self,
146
+ symtable: SymbolTable,
147
+ node: AST | None,
148
+ vars: MutableMapping[str, object] | None = None,
149
+ ) -> SymbolScope[object, TransformerScope]:
150
+ if isinstance(node, FunctionDef):
151
+ data = FunctionScope(node, self.scopes.scopes[-1].scope_data)
152
+ elif isinstance(node, ClassDef):
153
+ data = ClassScope()
154
+ else:
155
+ data = TransformerScope()
156
+ return SymbolScope(symtable, data)
157
+
158
+ def visit_Name(self, node: Name) -> None:
159
+ scope = self.scope_for(node.id).scope_data
160
+ if isinstance(node.ctx, ast.Load):
161
+ scope.loaded(node.id)
162
+ else:
163
+ scope.stored(node.id)
164
+
165
+ def visit_ExceptHandler(self, node: ExceptHandler) -> None:
166
+ self.generic_visit(node)
167
+ name = node.name
168
+ if name is not None:
169
+ self.scope_for(name).scope_data.stored(name)
170
+
171
+ def visit_Delete(self, node: Delete) -> None:
172
+ for target in node.targets:
173
+ if isinstance(target, ast.Name):
174
+ self.scope_for(target.id).scope_data.stored(target.id)
175
+
176
+ def visit_Global(self, node: Global) -> None:
177
+ if self.scopes.in_class_scope:
178
+ for name in node.names:
179
+ if name == "__annotations__":
180
+ self.error(
181
+ ["__annotations__"], node.lineno, node.col_offset, self.filename
182
+ )
183
+
184
+ def visit_ClassDef(self, node: ClassDef) -> None:
185
+ self.visit_Class_Outer(node)
186
+ class_scope = self.visit_Class_Inner(node).scope_data
187
+ assert isinstance(class_scope, ClassScope)
188
+
189
+ overlap = class_scope.instance_fields.intersection(class_scope.class_fields)
190
+ if overlap:
191
+ self.error(list(overlap), node.lineno, node.col_offset, self.filename)
192
+
193
+ self.scope_for(node.name).scope_data.stored(node.name)
194
+
195
+ def visit_FunctionDef(self, node: FunctionDef) -> None:
196
+ self.visit_Func_Outer(node)
197
+
198
+ func_scope = self.visit_Func_Inner(node)
199
+
200
+ self.scopes.current[node.name] = func_scope.scope_data
201
+
202
+ self.scope_for(node.name).scope_data.stored(node.name)
203
+
204
+ def visit_AsyncFunctionDef(self, node: AsyncFunctionDef) -> None:
205
+ self.visit_Func_Outer(node)
206
+
207
+ self.visit_Func_Inner(node)
208
+
209
+ self.scope_for(node.name).scope_data.stored(node.name)
210
+
211
+ def visit_Import(self, node: Import) -> None:
212
+ for name in node.names:
213
+ self.scope_for(imported_name(name)).scope_data.stored(imported_name(name))
214
+ return self.generic_visit(node)
215
+
216
+ def visit_ImportFrom(self, node: ImportFrom) -> None:
217
+ if node.level == 0 and node.module is not None:
218
+ for name in node.names:
219
+ self.scope_for(name.asname or name.name).scope_data.stored(
220
+ name.asname or name.name
221
+ )
222
+
223
+ def visit_Assign(self, node: Assign) -> None:
224
+ self.scopes.scopes[-1].scope_data.visit_Assign(node)
225
+ self.generic_visit(node)
226
+
227
+ def visit_AnnAssign(self, node: AnnAssign) -> None:
228
+ self.scopes.scopes[-1].scope_data.visit_AnnAssign(node)
229
+ value = node.value
230
+ if value is not None:
231
+ self.visit(node.target)
232
+ self.visit(value)
233
+ if not self.skip_annotations:
234
+ self.visit(node.annotation)
235
+
236
+ def visit_arg(self, node: ast.arg) -> None:
237
+ if not self.skip_annotations:
238
+ self.generic_visit(node)
239
+
240
+
241
+ def check_class_conflict(
242
+ node: Module,
243
+ filename: str,
244
+ symbols: SymbolTable,
245
+ ) -> None:
246
+ symbol_map = get_symbol_map(node, symbols)
247
+ flags = find_futures(0, node)
248
+ visitor = ClassConflictChecker(symbols, symbol_map, filename=filename, flags=flags)
249
+ visitor.visit(node)