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,40 @@
1
+ # Portions copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ # pyre-strict
4
+
5
+ """Package for compiling Python source code
6
+
7
+ There are several functions defined at the top level that are imported
8
+ from modules contained in the package.
9
+
10
+ compile(source, filename, mode, flags=None, dont_inherit=None)
11
+ Returns a code object. A replacement for the builtin compile() function.
12
+
13
+ compileFile(filename)
14
+ Generates a .pyc file by compiling filename.
15
+ """
16
+
17
+ import ast
18
+ from types import CodeType
19
+ from typing import Any
20
+
21
+ from .pycodegen import CinderCodeGenerator, compile, compile_code, compileFile
22
+
23
+
24
+ def exec_cinder(
25
+ source: str | bytes | ast.Module | ast.Expression | ast.Interactive | CodeType,
26
+ locals: dict[str, Any],
27
+ globals: dict[str, Any],
28
+ modname: str = "<module>",
29
+ ) -> None:
30
+ if isinstance(source, CodeType):
31
+ code = source
32
+ else:
33
+ code = compile_code(
34
+ source, "<module>", "exec", compiler=CinderCodeGenerator, modname=modname
35
+ )
36
+
37
+ exec(code, locals, globals)
38
+
39
+
40
+ __all__ = ("compile", "compile_code", "compileFile", "exec_cinder")
@@ -0,0 +1,137 @@
1
+ # Portions copyright (c) Meta Platforms, Inc. and affiliates.
2
+ # pyre-strict
3
+
4
+ import argparse
5
+ import builtins
6
+ import importlib.util
7
+ import marshal
8
+ import os
9
+ import re
10
+ import sys
11
+ from dis import dis
12
+ from types import CodeType
13
+ from typing import Pattern, TextIO
14
+
15
+ from .pycodegen import CinderCodeGenerator, CodeGenerator312, compile_code, make_header
16
+ from .static import FIXED_MODULES, StaticCodeGenerator
17
+ from .strict import StrictCodeGenerator
18
+
19
+ try:
20
+ from cinder import StrictModule
21
+ except ImportError:
22
+ StrictModule = None
23
+
24
+ # https://www.python.org/dev/peps/pep-0263/
25
+ coding_re: Pattern = re.compile(rb"^[ \t\f]*#.*?coding[:=][ \t]*([-_.a-zA-Z0-9]+)")
26
+
27
+
28
+ def open_with_coding(fname: str) -> TextIO:
29
+ with open(fname, "rb") as f:
30
+ line = f.readline()
31
+ m = coding_re.match(line)
32
+ if not m:
33
+ line = f.readline()
34
+ m = coding_re.match(line)
35
+ encoding = "utf-8"
36
+ if m:
37
+ encoding = m.group(1).decode()
38
+ return open(fname, encoding=encoding)
39
+
40
+
41
+ def main() -> None:
42
+ argparser = argparse.ArgumentParser(
43
+ prog="compiler",
44
+ description="Compile/execute a Python3 source file",
45
+ epilog="""\
46
+ By default, compile source code into in-memory code object and execute it.
47
+ If -c is specified, instead of executing write .pyc file.
48
+ """,
49
+ )
50
+ argparser.add_argument(
51
+ "-c", action="store_true", help="compile into .pyc file instead of executing"
52
+ )
53
+ argparser.add_argument(
54
+ "--dis", action="store_true", help="disassemble compiled code"
55
+ )
56
+ argparser.add_argument("--output", help="path to the output .pyc file")
57
+ argparser.add_argument(
58
+ "--modname",
59
+ help="module name to compile as (default __main__)",
60
+ default="__main__",
61
+ )
62
+ argparser.add_argument("input", help="source .py file")
63
+ group = argparser.add_mutually_exclusive_group()
64
+ group.add_argument(
65
+ "--static", action="store_true", help="compile using static compiler"
66
+ )
67
+ group.add_argument(
68
+ "--builtin", action="store_true", help="compile using built-in C compiler"
69
+ )
70
+ argparser.add_argument(
71
+ "--opt",
72
+ action="store",
73
+ type=int,
74
+ default=-1,
75
+ help="set optimization level to compile with",
76
+ )
77
+ argparser.add_argument("--strict", action="store_true", help="run in strict module")
78
+ args = argparser.parse_args()
79
+
80
+ with open_with_coding(args.input) as f:
81
+ fileinfo = os.stat(args.input)
82
+ source = f.read()
83
+
84
+ if args.builtin:
85
+ codeobj = compile(source, args.input, "exec")
86
+ assert isinstance(codeobj, CodeType)
87
+ else:
88
+ if args.static and args.strict:
89
+ raise ValueError("Cannot specify both --static and --strict options.")
90
+
91
+ compiler = (
92
+ StaticCodeGenerator
93
+ if args.static
94
+ else StrictCodeGenerator
95
+ if args.strict
96
+ else CinderCodeGenerator
97
+ )
98
+
99
+ codeobj = compile_code(
100
+ source,
101
+ args.input,
102
+ "exec",
103
+ optimize=args.opt,
104
+ compiler=compiler,
105
+ modname=args.modname,
106
+ )
107
+
108
+ if args.dis:
109
+ dis(codeobj)
110
+
111
+ if args.c:
112
+ if args.output:
113
+ name = args.output
114
+ else:
115
+ name = args.input.rsplit(".", 1)[0] + ".pyc"
116
+ with open(name, "wb") as f:
117
+ hdr = make_header(int(fileinfo.st_mtime), fileinfo.st_size)
118
+ f.write(importlib.util.MAGIC_NUMBER)
119
+ f.write(hdr)
120
+ marshal.dump(codeobj, f)
121
+ else:
122
+ if args.strict and StrictModule is not None:
123
+ d: dict[str, object] = {"__name__": "__main__"}
124
+ mod = StrictModule(d, False)
125
+ else:
126
+ mod = type(sys)("__main__")
127
+ d = mod.__dict__
128
+ if args.static or args.strict:
129
+ d["<fixed-modules>"] = FIXED_MODULES
130
+ d["<builtins>"] = builtins.__dict__
131
+ sys.modules["__main__"] = mod
132
+ # don't confuse the script with args meant for us
133
+ sys.argv = sys.argv[:1]
134
+ exec(codeobj, d, d)
135
+
136
+
137
+ main()
@@ -0,0 +1,7 @@
1
+ # Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ # Compile comprehensions as using their own inner scope
4
+ # (i.e. as lambdas).
5
+
6
+ # pyre-strict
7
+ COMPREHENSION_SCOPE = True
@@ -0,0 +1,72 @@
1
+ # Portions copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ # pyre-strict
4
+
5
+ import sys
6
+
7
+ # operation flags
8
+ OP_ASSIGN = "OP_ASSIGN"
9
+ OP_DELETE = "OP_DELETE"
10
+ OP_APPLY = "OP_APPLY"
11
+
12
+ SC_LOCAL = 1
13
+ SC_GLOBAL_IMPLICIT = 2
14
+ SC_GLOBAL_EXPLICIT = 3
15
+ SC_FREE = 4
16
+ SC_CELL = 5
17
+ SC_TYPE_PARAM = 9
18
+ SC_UNKNOWN = 6
19
+
20
+
21
+ CO_OPTIMIZED = 0x0001
22
+ CO_NEWLOCALS = 0x0002
23
+ CO_VARARGS = 0x0004
24
+ CO_VARKEYWORDS = 0x0008
25
+ CO_NESTED = 0x0010
26
+ CO_GENERATOR = 0x0020
27
+ CO_NOFREE = 0x0040
28
+ CO_COROUTINE = 0x0080
29
+ CO_GENERATOR_ALLOWED = 0
30
+ CO_ITERABLE_COROUTINE = 0x0100
31
+ CO_ASYNC_GENERATOR = 0x0200
32
+ CO_FUTURE_DIVISION = 0x20000
33
+ CO_FUTURE_ABSOLUTE_IMPORT = 0x40000
34
+ CO_FUTURE_WITH_STATEMENT = 0x80000
35
+ CO_FUTURE_PRINT_FUNCTION = 0x100000
36
+ CO_FUTURE_UNICODE_LITERALS = 0x200000
37
+ CO_FUTURE_BARRY_AS_BDFL = 0x400000
38
+ CO_FUTURE_GENERATOR_STOP = 0x800000
39
+ CO_FUTURE_ANNOTATIONS = 0x1000000
40
+ CO_HAS_DOCSTRING = 0x4000000
41
+ CO_METHOD = 0x8000000
42
+ if sys.version_info >= (3, 14):
43
+ CI_CO_STATICALLY_COMPILED = 0x4000
44
+ else:
45
+ CI_CO_STATICALLY_COMPILED = 0x4000000
46
+ CO_SUPPRESS_JIT = 0x40000000
47
+
48
+ PyCF_MASK_OBSOLETE: int = CO_NESTED
49
+ PyCF_SOURCE_IS_UTF8 = 0x0100
50
+ PyCF_DONT_IMPLY_DEDENT = 0x0200
51
+ PyCF_ONLY_AST = 0x0400
52
+ PyCF_IGNORE_COOKIE = 0x0800
53
+ PyCF_TYPE_COMMENTS = 0x1000
54
+ PyCF_ALLOW_TOP_LEVEL_AWAIT = 0x2000
55
+ PyCF_COMPILE_MASK: int = (
56
+ PyCF_ONLY_AST
57
+ | PyCF_ALLOW_TOP_LEVEL_AWAIT
58
+ | PyCF_TYPE_COMMENTS
59
+ | PyCF_DONT_IMPLY_DEDENT
60
+ )
61
+
62
+ PyCF_MASK: int = (
63
+ CO_FUTURE_DIVISION
64
+ | CO_FUTURE_ABSOLUTE_IMPORT
65
+ | CO_FUTURE_WITH_STATEMENT
66
+ | CO_FUTURE_PRINT_FUNCTION
67
+ | CO_FUTURE_UNICODE_LITERALS
68
+ | CO_FUTURE_BARRY_AS_BDFL
69
+ | CO_FUTURE_GENERATOR_STOP
70
+ | CO_FUTURE_ANNOTATIONS
71
+ | CI_CO_STATICALLY_COMPILED
72
+ )
@@ -0,0 +1,70 @@
1
+ # Copyright (c) Meta Platforms, Inc. and affiliates.
2
+
3
+ # pyre-strict
4
+ """Debugging output for various internal datatypes."""
5
+
6
+ from __future__ import annotations
7
+
8
+ from typing import TYPE_CHECKING
9
+
10
+ from .opcodes import opcode
11
+
12
+ if TYPE_CHECKING:
13
+ from .pyassem import Block, Instruction, PyFlowGraph
14
+
15
+
16
+ def str_of_oparg(instr: Instruction) -> str:
17
+ if instr.target is None:
18
+ # This is not a block
19
+ return str(instr.oparg)
20
+ elif instr.target.label:
21
+ # This is a labelled block
22
+ return f"{instr.target.bid} ({instr.target.label})"
23
+ else:
24
+ # This is an unlabelled block
25
+ return str(instr.target.bid)
26
+
27
+
28
+ def str_of_instr(instr: Instruction) -> str:
29
+ oparg = str_of_oparg(instr)
30
+ opname = instr.opname
31
+ return f"{instr.lineno} {opname} {oparg}"
32
+
33
+
34
+ def str_of_block_header(block: Block) -> str:
35
+ return repr(block)
36
+
37
+
38
+ def str_of_stack_effect(instr: Instruction) -> str:
39
+ d1 = opcode.stack_effect_raw(instr.opname, instr.oparg, False)
40
+ d2 = opcode.stack_effect_raw(instr.opname, instr.oparg, True)
41
+ if d1 != d2:
42
+ delta = f"{d1}/{d2}"
43
+ else:
44
+ delta = str(d1)
45
+ return delta
46
+
47
+
48
+ def str_of_block_instr(
49
+ instr: Instruction, pc: int = 0, stack_effect: bool = False
50
+ ) -> str:
51
+ delta = str_of_stack_effect(instr) if stack_effect else ""
52
+ eh = f" EH: {instr.exc_handler.bid}" if instr.exc_handler is not None else ""
53
+
54
+ return f"{delta:>6} | {pc:3} {str_of_instr(instr)}{eh}"
55
+
56
+
57
+ def dump_block(
58
+ graph: PyFlowGraph, block: Block, pc: int = 0, stack_effect: bool = False
59
+ ) -> int:
60
+ print(str_of_block_header(block))
61
+ for instr in block.getInstructions():
62
+ print(" ", str_of_block_instr(instr, pc, stack_effect))
63
+ pc += graph.instrsize(instr, instr.ioparg) * opcode.CODEUNIT_SIZE
64
+ return pc
65
+
66
+
67
+ def dump_graph(graph: PyFlowGraph, stack_effect: bool = False) -> None:
68
+ pc = 0
69
+ for block in graph.getBlocks():
70
+ pc = dump_block(graph, block, pc, stack_effect)
@@ -0,0 +1,283 @@
1
+ #!/usr/bin/env python3
2
+ # Portions copyright (c) Meta Platforms, Inc. and affiliates.
3
+ #
4
+ # Dissassemble code objects:
5
+ # a) recursively (like dis.dis() in CPython behaves);
6
+ # b) providing stable references to internal code objects (by replacing
7
+ # memory address with incrementing number);
8
+ # c) besides disassembly, also dump other fields of code objects.
9
+ # Useful for comparing disassembly outputs from different runs.
10
+ #
11
+
12
+ # pyre-strict
13
+
14
+ import dis as _dis
15
+ import re
16
+ import sys
17
+ from collections.abc import Generator, Iterable
18
+ from pprint import pformat
19
+ from re import Pattern
20
+ from types import CodeType
21
+ from typing import Optional, TextIO
22
+
23
+
24
+ def _make_stable(
25
+ gen: Iterable[_dis.Instruction],
26
+ ) -> Generator[_dis.Instruction, None, None]:
27
+ for instr in gen:
28
+ if sys.version_info >= (3, 14):
29
+ yield _dis.Instruction.make(
30
+ instr.opname,
31
+ instr.arg,
32
+ instr.argval,
33
+ _stable_repr(instr.argval),
34
+ instr.offset,
35
+ instr.start_offset,
36
+ instr.starts_line,
37
+ instr.line_number,
38
+ instr.label,
39
+ instr.positions,
40
+ instr.cache_info,
41
+ )
42
+ else:
43
+ yield _dis.Instruction(
44
+ instr.opname,
45
+ instr.opcode,
46
+ instr.arg,
47
+ instr.argval,
48
+ _stable_repr(instr.argval),
49
+ instr.offset,
50
+ instr.starts_line,
51
+ instr.is_jump_target,
52
+ )
53
+
54
+
55
+ def _stable_repr(obj: object) -> str:
56
+ if isinstance(obj, frozenset):
57
+ obj = frozenset(sorted(obj, key=repr))
58
+ return repr(obj)
59
+
60
+
61
+ def _disassemble_bytes(
62
+ co: CodeType,
63
+ code: bytes,
64
+ lasti: int = -1,
65
+ varnames: Optional[tuple[str]] = None,
66
+ names: Optional[tuple[str]] = None,
67
+ constants: Optional[tuple[object]] = None,
68
+ cells: Optional[tuple[object]] = None,
69
+ linestarts: Optional[dict[int, int]] = None,
70
+ *,
71
+ file: Optional[TextIO] = None,
72
+ line_offset: int = 0,
73
+ localsplusnames: Optional[tuple[str]] = None,
74
+ ) -> None:
75
+ # Omit the line number column entirely if we have no line number info
76
+ show_lineno = linestarts is not None
77
+ if show_lineno:
78
+ # pyre-fixme [16]: `Optional` has no attribute `values`.
79
+ maxlineno = max(linestarts.values()) + line_offset
80
+ if maxlineno >= 1000:
81
+ lineno_width = len(str(maxlineno))
82
+ else:
83
+ lineno_width = 3
84
+ else:
85
+ lineno_width = 0
86
+ maxoffset = len(code) - 2
87
+ if maxoffset >= 10000:
88
+ offset_width = len(str(maxoffset))
89
+ else:
90
+ offset_width = 4
91
+ if sys.version_info >= (3, 14):
92
+ if co is not None:
93
+ exception_entries = _dis._parse_exception_table(co)
94
+ labels_map = _dis._make_labels_map(
95
+ code, exception_entries=exception_entries
96
+ )
97
+ else:
98
+ labels_map = None
99
+ instr_bytes = _dis._get_instructions_bytes(
100
+ code,
101
+ arg_resolver=_dis.ArgResolver(
102
+ constants, names, lambda oparg: localsplusnames[oparg], labels_map
103
+ ),
104
+ linestarts=linestarts,
105
+ line_offset=line_offset,
106
+ )
107
+ elif sys.version_info >= (3, 12):
108
+ # pyre-fixme[16]: Module `dis` has no attribute `_get_instructions_bytes`.
109
+ instr_bytes = _dis._get_instructions_bytes(
110
+ code,
111
+ # pyre-fixme[16]: `Optional` has no attribute `__getitem__`.
112
+ lambda oparg: localsplusnames[oparg],
113
+ names,
114
+ constants,
115
+ linestarts,
116
+ line_offset=line_offset,
117
+ )
118
+ else:
119
+ # pyre-fixme [16]: Module `dis` has no attribute `_get_instructions_bytes`
120
+ instr_bytes = _dis._get_instructions_bytes(
121
+ code, varnames, names, constants, cells, linestarts, line_offset=line_offset
122
+ )
123
+ for instr in _make_stable(instr_bytes):
124
+ new_source_line = (
125
+ show_lineno and instr.starts_line is not None and instr.offset > 0
126
+ )
127
+ if new_source_line:
128
+ print(file=file)
129
+ is_current_instr = instr.offset == lasti
130
+
131
+ if sys.version_info >= (3, 14):
132
+ _dis.Formatter(
133
+ file=file, lineno_width=lineno_width, offset_width=offset_width
134
+ ).print_instruction(instr, is_current_instr)
135
+ else:
136
+ print(
137
+ instr._disassemble(lineno_width, is_current_instr, offset_width),
138
+ file=file,
139
+ )
140
+
141
+
142
+ def disassemble(
143
+ co: CodeType,
144
+ lasti: int = -1,
145
+ *,
146
+ file: Optional[TextIO] = None,
147
+ skip_line_nos: bool = False,
148
+ ) -> None:
149
+ cell_names = co.co_cellvars + co.co_freevars
150
+ if skip_line_nos:
151
+ linestarts = None
152
+ else:
153
+ linestarts = dict(_dis.findlinestarts(co))
154
+ localsplusnames = (
155
+ co.co_varnames
156
+ if sys.version_info < (3, 12)
157
+ else (co.co_varnames + co.co_cellvars + co.co_freevars)
158
+ )
159
+ _disassemble_bytes(
160
+ co,
161
+ co.co_code,
162
+ lasti,
163
+ co.co_varnames,
164
+ co.co_names,
165
+ co.co_consts,
166
+ cell_names,
167
+ linestarts,
168
+ file=file,
169
+ localsplusnames=localsplusnames,
170
+ )
171
+
172
+
173
+ class Disassembler:
174
+ def __init__(self) -> None:
175
+ self.id_map: dict[int, int] = {}
176
+ self.id_cnt: int = 0
177
+
178
+ def get_co_id(self, co: CodeType) -> int:
179
+ addr = id(co)
180
+ if addr in self.id_map:
181
+ return self.id_map[addr]
182
+ self.id_map[addr] = self.id_cnt
183
+ self.id_cnt += 1
184
+ return self.id_cnt - 1
185
+
186
+ def co_repr(self, co: CodeType) -> str:
187
+ return '<code object %s at #%d, file "%s", line %d>' % (
188
+ co.co_name,
189
+ self.get_co_id(co),
190
+ co.co_filename,
191
+ co.co_firstlineno,
192
+ )
193
+
194
+ def disassemble(
195
+ self,
196
+ co: CodeType,
197
+ lasti: int = -1,
198
+ file: Optional[TextIO] = None,
199
+ skip_line_nos: bool = False,
200
+ ) -> None:
201
+ """Disassemble a code object."""
202
+ consts = tuple(
203
+ [self.co_repr(x) if hasattr(x, "co_code") else x for x in co.co_consts]
204
+ )
205
+ codeobj = co.replace(co_consts=consts)
206
+ disassemble(codeobj, file=file, skip_line_nos=skip_line_nos)
207
+
208
+ def dump_code(self, co: CodeType, file: Optional[TextIO] = None) -> None:
209
+ if not file:
210
+ file = sys.stdout
211
+ print(self.co_repr(co), file=file)
212
+ self.disassemble(co, file=file, skip_line_nos=True)
213
+ print("co_argcount:", co.co_argcount, file=file)
214
+ print("co_kwonlyargcount:", co.co_kwonlyargcount, file=file)
215
+ print("co_stacksize:", co.co_stacksize, file=file)
216
+ flags = []
217
+ co_flags = co.co_flags
218
+ for val, name in _dis.COMPILER_FLAG_NAMES.items():
219
+ if co_flags & val:
220
+ flags.append(name)
221
+ co_flags &= ~val
222
+ if co_flags:
223
+ flags.append(hex(co_flags))
224
+ print("co_flags:", hex(co.co_flags), "(" + " | ".join(flags) + ")", file=file)
225
+ print(
226
+ "co_consts:",
227
+ pformat(
228
+ tuple(
229
+ [
230
+ self.co_repr(x) if hasattr(x, "co_code") else _stable_repr(x)
231
+ for x in co.co_consts
232
+ ]
233
+ )
234
+ ),
235
+ file=file,
236
+ )
237
+ print("co_firstlineno:", co.co_firstlineno, file=file)
238
+ print("co_names:", co.co_names, file=file)
239
+ print("co_varnames:", co.co_varnames, file=file)
240
+ print("co_cellvars:", co.co_cellvars, file=file)
241
+ print("co_freevars:", co.co_freevars, file=file)
242
+ print("co_lines:", pformat(list(co.co_lines())), file=file)
243
+ if sys.version_info >= (3, 12):
244
+ print("co_positions:", file=file)
245
+ for i, position in enumerate(co.co_positions()):
246
+ print(f"Offset {i * 2}: {position}", file=file)
247
+ print("co_exceptiontable:", co.co_exceptiontable, file=file)
248
+ print("exception table: ", file=file)
249
+ print(
250
+ # pyre-fixme[16]: Module `dis` has no attribute
251
+ # `_parse_exception_table`.
252
+ "\n".join(" " + str(x) for x in _dis._parse_exception_table(co)),
253
+ file=file,
254
+ )
255
+ print(file=file)
256
+ for c in co.co_consts:
257
+ if hasattr(c, "co_code"):
258
+ self.dump_code(c, file)
259
+
260
+
261
+ # https://www.python.org/dev/peps/pep-0263/
262
+ coding_re: Pattern[bytes] = re.compile(
263
+ rb"^[ \t\f]*#.*?coding[:=][ \t]*([-_.a-zA-Z0-9]+)"
264
+ )
265
+
266
+
267
+ def open_with_coding(fname: str) -> TextIO:
268
+ with open(fname, "rb") as f:
269
+ line = f.readline()
270
+ m = coding_re.match(line)
271
+ if not m:
272
+ line = f.readline()
273
+ m = coding_re.match(line)
274
+ encoding = "utf-8"
275
+ if m:
276
+ encoding = m.group(1).decode()
277
+ return open(fname, encoding=encoding)
278
+
279
+
280
+ if __name__ == "__main__":
281
+ with open_with_coding(sys.argv[1]) as f:
282
+ co: CodeType = compile(f.read(), sys.argv[1], "exec")
283
+ Disassembler().dump_code(co, file=sys.stdout)