bytecode 0.16.0__tar.gz → 0.16.1__tar.gz
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.
- {bytecode-0.16.0 → bytecode-0.16.1}/.github/workflows/cis.yml +1 -1
- {bytecode-0.16.0/src/bytecode.egg-info → bytecode-0.16.1}/PKG-INFO +2 -2
- {bytecode-0.16.0 → bytecode-0.16.1}/doc/changelog.rst +7 -0
- {bytecode-0.16.0 → bytecode-0.16.1}/src/bytecode/cfg.py +1 -1
- {bytecode-0.16.0 → bytecode-0.16.1}/src/bytecode/concrete.py +2 -2
- {bytecode-0.16.0 → bytecode-0.16.1}/src/bytecode/flags.py +98 -48
- {bytecode-0.16.0 → bytecode-0.16.1}/src/bytecode/instr.py +7 -12
- {bytecode-0.16.0 → bytecode-0.16.1}/src/bytecode/version.py +2 -2
- {bytecode-0.16.0 → bytecode-0.16.1/src/bytecode.egg-info}/PKG-INFO +2 -2
- {bytecode-0.16.0 → bytecode-0.16.1}/tests/test_cfg.py +3 -9
- {bytecode-0.16.0 → bytecode-0.16.1}/tests/test_flags.py +96 -40
- {bytecode-0.16.0 → bytecode-0.16.1}/.coveragerc +0 -0
- {bytecode-0.16.0 → bytecode-0.16.1}/.github/FUNDING.yml +0 -0
- {bytecode-0.16.0 → bytecode-0.16.1}/.github/dependabot.yml +0 -0
- {bytecode-0.16.0 → bytecode-0.16.1}/.github/workflows/docs.yml +0 -0
- {bytecode-0.16.0 → bytecode-0.16.1}/.github/workflows/frameworks.yml +0 -0
- {bytecode-0.16.0 → bytecode-0.16.1}/.github/workflows/release.yml +0 -0
- {bytecode-0.16.0 → bytecode-0.16.1}/.gitignore +0 -0
- {bytecode-0.16.0 → bytecode-0.16.1}/.pre-commit-config.yaml +0 -0
- {bytecode-0.16.0 → bytecode-0.16.1}/.readthedocs.yaml +0 -0
- {bytecode-0.16.0 → bytecode-0.16.1}/COPYING +0 -0
- {bytecode-0.16.0 → bytecode-0.16.1}/MANIFEST.in +0 -0
- {bytecode-0.16.0 → bytecode-0.16.1}/README.rst +0 -0
- {bytecode-0.16.0 → bytecode-0.16.1}/TODO.rst +0 -0
- {bytecode-0.16.0 → bytecode-0.16.1}/codecov.yml +0 -0
- {bytecode-0.16.0 → bytecode-0.16.1}/doc/Makefile +0 -0
- {bytecode-0.16.0 → bytecode-0.16.1}/doc/api.rst +0 -0
- {bytecode-0.16.0 → bytecode-0.16.1}/doc/byteplay_codetransformer.rst +0 -0
- {bytecode-0.16.0 → bytecode-0.16.1}/doc/cfg.rst +0 -0
- {bytecode-0.16.0 → bytecode-0.16.1}/doc/conf.py +0 -0
- {bytecode-0.16.0 → bytecode-0.16.1}/doc/index.rst +0 -0
- {bytecode-0.16.0 → bytecode-0.16.1}/doc/make.bat +0 -0
- {bytecode-0.16.0 → bytecode-0.16.1}/doc/requirements.txt +0 -0
- {bytecode-0.16.0 → bytecode-0.16.1}/doc/todo.rst +0 -0
- {bytecode-0.16.0 → bytecode-0.16.1}/doc/usage.rst +0 -0
- {bytecode-0.16.0 → bytecode-0.16.1}/pyproject.toml +0 -0
- {bytecode-0.16.0 → bytecode-0.16.1}/scripts/frameworks/boto3/run.sh +0 -0
- {bytecode-0.16.0 → bytecode-0.16.1}/scripts/frameworks/boto3/setup.sh +0 -0
- {bytecode-0.16.0 → bytecode-0.16.1}/setup.cfg +0 -0
- {bytecode-0.16.0 → bytecode-0.16.1}/src/bytecode/__init__.py +7 -7
- {bytecode-0.16.0 → bytecode-0.16.1}/src/bytecode/bytecode.py +0 -0
- {bytecode-0.16.0 → bytecode-0.16.1}/src/bytecode/py.typed +0 -0
- {bytecode-0.16.0 → bytecode-0.16.1}/src/bytecode/utils.py +0 -0
- {bytecode-0.16.0 → bytecode-0.16.1}/src/bytecode.egg-info/SOURCES.txt +0 -0
- {bytecode-0.16.0 → bytecode-0.16.1}/src/bytecode.egg-info/dependency_links.txt +0 -0
- {bytecode-0.16.0 → bytecode-0.16.1}/src/bytecode.egg-info/requires.txt +0 -0
- {bytecode-0.16.0 → bytecode-0.16.1}/src/bytecode.egg-info/top_level.txt +0 -0
- {bytecode-0.16.0 → bytecode-0.16.1}/tests/__init__.py +0 -0
- {bytecode-0.16.0 → bytecode-0.16.1}/tests/cell_free_vars_cases.py +0 -0
- {bytecode-0.16.0 → bytecode-0.16.1}/tests/exception_handling_cases.py +0 -0
- {bytecode-0.16.0 → bytecode-0.16.1}/tests/frameworks/function.py +0 -0
- {bytecode-0.16.0 → bytecode-0.16.1}/tests/frameworks/module.py +0 -0
- {bytecode-0.16.0 → bytecode-0.16.1}/tests/frameworks/sitecustomize.py +0 -0
- {bytecode-0.16.0 → bytecode-0.16.1}/tests/long_lines_example.py +0 -0
- {bytecode-0.16.0 → bytecode-0.16.1}/tests/test_bytecode.py +0 -0
- {bytecode-0.16.0 → bytecode-0.16.1}/tests/test_code.py +0 -0
- {bytecode-0.16.0 → bytecode-0.16.1}/tests/test_concrete.py +0 -0
- {bytecode-0.16.0 → bytecode-0.16.1}/tests/test_instr.py +0 -0
- {bytecode-0.16.0 → bytecode-0.16.1}/tests/test_misc.py +0 -0
- {bytecode-0.16.0 → bytecode-0.16.1}/tests/util_annotation.py +0 -0
- {bytecode-0.16.0 → bytecode-0.16.1}/tox.ini +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
2
|
Name: bytecode
|
|
3
|
-
Version: 0.16.
|
|
3
|
+
Version: 0.16.1
|
|
4
4
|
Summary: Python module to generate and modify bytecode
|
|
5
5
|
Author-email: Victor Stinner <victor.stinner@gmail.com>
|
|
6
6
|
Maintainer-email: "Matthieu C. Dartiailh" <m.dartiailh@gmail.com>
|
|
@@ -58,7 +58,7 @@ class BasicBlock(_bytecode._InstrList[Union[Instr, SetLineno, TryBegin, TryEnd]]
|
|
|
58
58
|
isinstance(self[i], Instr) for i in range(index, len(self))
|
|
59
59
|
):
|
|
60
60
|
raise ValueError(
|
|
61
|
-
"Only the last instruction of a basic
|
|
61
|
+
"Only the last instruction of a basic block can be a jump"
|
|
62
62
|
)
|
|
63
63
|
|
|
64
64
|
if not isinstance(instr.arg, BasicBlock):
|
|
@@ -83,7 +83,7 @@ class ConcreteInstr(BaseInstr[int]):
|
|
|
83
83
|
# For ConcreteInstr the argument is always an integer
|
|
84
84
|
_arg: int
|
|
85
85
|
|
|
86
|
-
__slots__ = ("
|
|
86
|
+
__slots__ = ("_extended_args", "_size")
|
|
87
87
|
|
|
88
88
|
def __init__(
|
|
89
89
|
self,
|
|
@@ -221,7 +221,7 @@ class ExceptionTableEntry:
|
|
|
221
221
|
#: before the exception itself (which is pushed as a single value)).
|
|
222
222
|
push_lasti: bool
|
|
223
223
|
|
|
224
|
-
__slots__ = ("
|
|
224
|
+
__slots__ = ("push_lasti", "stack_depth", "start_offset", "stop_offset", "target")
|
|
225
225
|
|
|
226
226
|
def __init__(
|
|
227
227
|
self,
|
|
@@ -1,11 +1,13 @@
|
|
|
1
|
-
import opcode
|
|
2
|
-
import sys
|
|
1
|
+
import opcode as _opcode
|
|
3
2
|
from enum import IntFlag
|
|
4
3
|
from typing import Optional, Union
|
|
5
4
|
|
|
6
5
|
# alias to keep the 'bytecode' variable free
|
|
7
6
|
import bytecode as _bytecode
|
|
8
7
|
|
|
8
|
+
from .instr import DUAL_ARG_OPCODES, CellVar, FreeVar
|
|
9
|
+
from .utils import PY311, PY312, PY313
|
|
10
|
+
|
|
9
11
|
|
|
10
12
|
class CompilerFlags(IntFlag):
|
|
11
13
|
"""Possible values of the co_flags attribute of Code object.
|
|
@@ -32,14 +34,31 @@ class CompilerFlags(IntFlag):
|
|
|
32
34
|
# Generator defined in an async def function
|
|
33
35
|
ASYNC_GENERATOR = 0x00200
|
|
34
36
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
37
|
+
FUTURE_GENERATOR_STOP = 0x800000
|
|
38
|
+
FUTURE_ANNOTATIONS = 0x1000000
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
UNOPTIMIZED_OPCODES = (
|
|
42
|
+
_opcode.opmap["STORE_NAME"],
|
|
43
|
+
_opcode.opmap["LOAD_NAME"],
|
|
44
|
+
_opcode.opmap["DELETE_NAME"],
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
ASYNC_OPCODES = (
|
|
48
|
+
_opcode.opmap["GET_AWAITABLE"],
|
|
49
|
+
_opcode.opmap["GET_AITER"],
|
|
50
|
+
_opcode.opmap["GET_ANEXT"],
|
|
51
|
+
_opcode.opmap["BEFORE_ASYNC_WITH"],
|
|
52
|
+
*((_opcode.opmap["SETUP_ASYNC_WITH"],) if not PY311 else ()), # Removed in 3.11+
|
|
53
|
+
_opcode.opmap["END_ASYNC_FOR"],
|
|
54
|
+
*((_opcode.opmap["ASYNC_GEN_WRAP"],) if PY311 and not PY312 else ()), # New in 3.11
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
YIELD_VALUE_OPCODE = _opcode.opmap["YIELD_VALUE"]
|
|
58
|
+
GENERATOR_LIKE_OPCODES = (
|
|
59
|
+
*((_opcode.opmap["YIELD_FROM"],) if not PY311 else ()), # Removed in 3.11+
|
|
60
|
+
*((_opcode.opmap["RETURN_GENERATOR"],) if PY311 else ()), # Added in 3.11+
|
|
61
|
+
)
|
|
43
62
|
|
|
44
63
|
|
|
45
64
|
def infer_flags(
|
|
@@ -70,8 +89,7 @@ def infer_flags(
|
|
|
70
89
|
(_bytecode.Bytecode, _bytecode.ConcreteBytecode, _bytecode.ControlFlowGraph),
|
|
71
90
|
):
|
|
72
91
|
msg = (
|
|
73
|
-
"Expected a Bytecode, ConcreteBytecode or ControlFlowGraph "
|
|
74
|
-
"instance not %s"
|
|
92
|
+
"Expected a Bytecode, ConcreteBytecode or ControlFlowGraph instance not %s"
|
|
75
93
|
)
|
|
76
94
|
raise ValueError(msg % bytecode)
|
|
77
95
|
|
|
@@ -80,26 +98,73 @@ def infer_flags(
|
|
|
80
98
|
if isinstance(bytecode, _bytecode.ControlFlowGraph)
|
|
81
99
|
else bytecode
|
|
82
100
|
)
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
101
|
+
|
|
102
|
+
# Iterate over the instructions and inspect the arguments
|
|
103
|
+
is_concrete = isinstance(bytecode, _bytecode.ConcreteBytecode)
|
|
104
|
+
optimized = True
|
|
105
|
+
has_free = False if not is_concrete else bytecode.cellvars and bytecode.freevars
|
|
106
|
+
known_async = False
|
|
107
|
+
known_generator = False
|
|
108
|
+
possible_generator = False
|
|
109
|
+
instr_iter = iter(instructions)
|
|
110
|
+
for instr in instr_iter:
|
|
111
|
+
if isinstance(
|
|
112
|
+
instr,
|
|
88
113
|
(
|
|
89
114
|
_bytecode.SetLineno,
|
|
90
115
|
_bytecode.Label,
|
|
91
116
|
_bytecode.TryBegin,
|
|
92
117
|
_bytecode.TryEnd,
|
|
93
118
|
),
|
|
94
|
-
)
|
|
95
|
-
|
|
119
|
+
):
|
|
120
|
+
continue
|
|
121
|
+
opcode = instr.opcode
|
|
122
|
+
if opcode in UNOPTIMIZED_OPCODES:
|
|
123
|
+
optimized = False
|
|
124
|
+
elif opcode in ASYNC_OPCODES:
|
|
125
|
+
known_async = True
|
|
126
|
+
elif opcode == YIELD_VALUE_OPCODE:
|
|
127
|
+
if PY311:
|
|
128
|
+
while isinstance(
|
|
129
|
+
ni := next(instr_iter),
|
|
130
|
+
(
|
|
131
|
+
_bytecode.SetLineno,
|
|
132
|
+
_bytecode.Label,
|
|
133
|
+
_bytecode.TryBegin,
|
|
134
|
+
_bytecode.TryEnd,
|
|
135
|
+
),
|
|
136
|
+
):
|
|
137
|
+
pass
|
|
138
|
+
assert ni.name == "RESUME"
|
|
139
|
+
if (ni.arg & 3) != 3:
|
|
140
|
+
known_generator = True
|
|
141
|
+
else:
|
|
142
|
+
known_async = True
|
|
143
|
+
else:
|
|
144
|
+
known_generator = True
|
|
145
|
+
elif opcode in GENERATOR_LIKE_OPCODES:
|
|
146
|
+
possible_generator = True
|
|
147
|
+
elif opcode in _opcode.hasfree:
|
|
148
|
+
has_free = True
|
|
149
|
+
elif (
|
|
150
|
+
not is_concrete
|
|
151
|
+
and opcode in DUAL_ARG_OPCODES
|
|
152
|
+
and (isinstance(instr.arg[0], CellVar) or isinstance(instr.arg[1], CellVar))
|
|
153
|
+
):
|
|
154
|
+
has_free = True
|
|
155
|
+
elif (
|
|
156
|
+
PY313
|
|
157
|
+
and opcode in _opcode.haslocal
|
|
158
|
+
and isinstance(instr.arg, (CellVar, FreeVar))
|
|
159
|
+
):
|
|
160
|
+
has_free = True
|
|
96
161
|
|
|
97
162
|
# Identify optimized code
|
|
98
|
-
if
|
|
163
|
+
if optimized:
|
|
99
164
|
flags |= CompilerFlags.OPTIMIZED
|
|
100
165
|
|
|
101
166
|
# Check for free variables
|
|
102
|
-
if not
|
|
167
|
+
if not has_free:
|
|
103
168
|
flags |= CompilerFlags.NOFREE
|
|
104
169
|
|
|
105
170
|
# Copy flags for which we cannot infer the right value
|
|
@@ -110,29 +175,19 @@ def infer_flags(
|
|
|
110
175
|
| CompilerFlags.NESTED
|
|
111
176
|
)
|
|
112
177
|
|
|
113
|
-
sure_generator = instr_names & {"YIELD_VALUE"}
|
|
114
|
-
maybe_generator = instr_names & {"YIELD_VALUE", "YIELD_FROM", "RETURN_GENERATOR"}
|
|
115
|
-
|
|
116
|
-
sure_async = instr_names & {
|
|
117
|
-
"GET_AWAITABLE",
|
|
118
|
-
"GET_AITER",
|
|
119
|
-
"GET_ANEXT",
|
|
120
|
-
"BEFORE_ASYNC_WITH",
|
|
121
|
-
"SETUP_ASYNC_WITH",
|
|
122
|
-
"END_ASYNC_FOR",
|
|
123
|
-
"ASYNC_GEN_WRAP", # New in 3.11
|
|
124
|
-
}
|
|
125
|
-
|
|
126
178
|
# If performing inference or forcing an async behavior, first inspect
|
|
127
179
|
# the flags since this is the only way to identify iterable coroutines
|
|
128
180
|
if is_async in (None, True):
|
|
129
|
-
if
|
|
130
|
-
|
|
181
|
+
if (
|
|
182
|
+
bytecode.flags & CompilerFlags.COROUTINE
|
|
183
|
+
or bytecode.flags & CompilerFlags.ASYNC_GENERATOR
|
|
184
|
+
):
|
|
185
|
+
if known_generator:
|
|
131
186
|
flags |= CompilerFlags.ASYNC_GENERATOR
|
|
132
187
|
else:
|
|
133
188
|
flags |= CompilerFlags.COROUTINE
|
|
134
189
|
elif bytecode.flags & CompilerFlags.ITERABLE_COROUTINE:
|
|
135
|
-
if
|
|
190
|
+
if known_async:
|
|
136
191
|
msg = (
|
|
137
192
|
"The ITERABLE_COROUTINE flag is set but bytecode that"
|
|
138
193
|
"can only be used in async functions have been "
|
|
@@ -141,25 +196,20 @@ def infer_flags(
|
|
|
141
196
|
)
|
|
142
197
|
raise ValueError(msg)
|
|
143
198
|
flags |= CompilerFlags.ITERABLE_COROUTINE
|
|
144
|
-
elif bytecode.flags & CompilerFlags.ASYNC_GENERATOR:
|
|
145
|
-
if not sure_generator:
|
|
146
|
-
flags |= CompilerFlags.COROUTINE
|
|
147
|
-
else:
|
|
148
|
-
flags |= CompilerFlags.ASYNC_GENERATOR
|
|
149
199
|
|
|
150
200
|
# If the code was not asynchronous before determine if it should now be
|
|
151
201
|
# asynchronous based on the opcode and the is_async argument.
|
|
152
202
|
else:
|
|
153
|
-
if
|
|
203
|
+
if known_async:
|
|
154
204
|
# YIELD_FROM is not allowed in async generator
|
|
155
|
-
if
|
|
205
|
+
if known_generator:
|
|
156
206
|
flags |= CompilerFlags.ASYNC_GENERATOR
|
|
157
207
|
else:
|
|
158
208
|
flags |= CompilerFlags.COROUTINE
|
|
159
209
|
|
|
160
|
-
elif
|
|
210
|
+
elif known_generator or possible_generator:
|
|
161
211
|
if is_async:
|
|
162
|
-
if
|
|
212
|
+
if known_generator:
|
|
163
213
|
flags |= CompilerFlags.ASYNC_GENERATOR
|
|
164
214
|
else:
|
|
165
215
|
flags |= CompilerFlags.COROUTINE
|
|
@@ -172,14 +222,14 @@ def infer_flags(
|
|
|
172
222
|
# If the code should not be asynchronous, check first it is possible and
|
|
173
223
|
# next set the GENERATOR flag if relevant
|
|
174
224
|
else:
|
|
175
|
-
if
|
|
225
|
+
if known_async:
|
|
176
226
|
raise ValueError(
|
|
177
227
|
"The is_async argument is False but bytecodes "
|
|
178
228
|
"that can only be used in async functions have "
|
|
179
229
|
"been detected."
|
|
180
230
|
)
|
|
181
231
|
|
|
182
|
-
if
|
|
232
|
+
if known_generator or possible_generator:
|
|
183
233
|
flags |= CompilerFlags.GENERATOR
|
|
184
234
|
|
|
185
235
|
flags |= bytecode.flags & CompilerFlags.FUTURE_GENERATOR_STOP
|
|
@@ -269,13 +269,12 @@ class FreeVar(_Variable):
|
|
|
269
269
|
def _check_arg_int(arg: Any, name: str) -> TypeGuard[int]:
|
|
270
270
|
if not isinstance(arg, int):
|
|
271
271
|
raise TypeError(
|
|
272
|
-
"operation %s argument must be an int, "
|
|
273
|
-
"got %s" % (name, type(arg).__name__)
|
|
272
|
+
"operation %s argument must be an int, got %s" % (name, type(arg).__name__)
|
|
274
273
|
)
|
|
275
274
|
|
|
276
275
|
if not (0 <= arg <= 2147483647):
|
|
277
276
|
raise ValueError(
|
|
278
|
-
"operation %s argument must be in
|
|
277
|
+
"operation %s argument must be in the range 0..2,147,483,647" % name
|
|
279
278
|
)
|
|
280
279
|
|
|
281
280
|
return True
|
|
@@ -441,7 +440,7 @@ class InstrLocation:
|
|
|
441
440
|
#: End column offset at which the instruction corresponds (Python 3.11+ only)
|
|
442
441
|
end_col_offset: Optional[int]
|
|
443
442
|
|
|
444
|
-
__slots__ = ["
|
|
443
|
+
__slots__ = ["col_offset", "end_col_offset", "end_lineno", "lineno"]
|
|
445
444
|
|
|
446
445
|
def __init__(
|
|
447
446
|
self,
|
|
@@ -523,7 +522,7 @@ class SetLineno:
|
|
|
523
522
|
|
|
524
523
|
|
|
525
524
|
class TryBegin:
|
|
526
|
-
__slots__ = ("
|
|
525
|
+
__slots__ = ("push_lasti", "stack_depth", "target")
|
|
527
526
|
|
|
528
527
|
def __init__(
|
|
529
528
|
self,
|
|
@@ -556,7 +555,7 @@ A = TypeVar("A", bound=object)
|
|
|
556
555
|
class BaseInstr(Generic[A]):
|
|
557
556
|
"""Abstract instruction."""
|
|
558
557
|
|
|
559
|
-
__slots__ = ("
|
|
558
|
+
__slots__ = ("_arg", "_location", "_name", "_opcode")
|
|
560
559
|
|
|
561
560
|
# Work around an issue with the default value of arg
|
|
562
561
|
def __init__(
|
|
@@ -900,13 +899,9 @@ class Instr(BaseInstr[InstrArg]):
|
|
|
900
899
|
|
|
901
900
|
elif opcode in _opcode.hasconst:
|
|
902
901
|
if isinstance(arg, Label):
|
|
903
|
-
raise ValueError(
|
|
904
|
-
"label argument cannot be used " "in %s operation" % name
|
|
905
|
-
)
|
|
902
|
+
raise ValueError("label argument cannot be used in %s operation" % name)
|
|
906
903
|
if isinstance(arg, _bytecode.BasicBlock):
|
|
907
|
-
raise ValueError(
|
|
908
|
-
"block argument cannot be used " "in %s operation" % name
|
|
909
|
-
)
|
|
904
|
+
raise ValueError("block argument cannot be used in %s operation" % name)
|
|
910
905
|
|
|
911
906
|
elif opcode in _opcode.hascompare:
|
|
912
907
|
if not isinstance(arg, Compare):
|
|
@@ -5,7 +5,7 @@ from collections import namedtuple
|
|
|
5
5
|
#: A namedtuple of the version info for the current release.
|
|
6
6
|
_version_info = namedtuple("_version_info", "major minor micro status")
|
|
7
7
|
|
|
8
|
-
parts = "0.16.
|
|
8
|
+
parts = "0.16.1".split(".", 3)
|
|
9
9
|
version_info = _version_info(
|
|
10
10
|
int(parts[0]),
|
|
11
11
|
int(parts[1]),
|
|
@@ -16,4 +16,4 @@ version_info = _version_info(
|
|
|
16
16
|
# Remove everything but the 'version_info' from this module.
|
|
17
17
|
del namedtuple, _version_info, parts
|
|
18
18
|
|
|
19
|
-
__version__ = "0.16.
|
|
19
|
+
__version__ = "0.16.1"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
2
|
Name: bytecode
|
|
3
|
-
Version: 0.16.
|
|
3
|
+
Version: 0.16.1
|
|
4
4
|
Summary: Python module to generate and modify bytecode
|
|
5
5
|
Author-email: Victor Stinner <victor.stinner@gmail.com>
|
|
6
6
|
Maintainer-email: "Matthieu C. Dartiailh" <m.dartiailh@gmail.com>
|
|
@@ -668,18 +668,12 @@ class BytecodeBlocksFunctionalTests(TestCase):
|
|
|
668
668
|
)
|
|
669
669
|
elif PY311:
|
|
670
670
|
# jump is relative not absolute
|
|
671
|
-
expected =
|
|
672
|
-
b"|\x05" b"r\x02" b"|\x00" b"}\x05" b"d\x01" b"}\x05" b"|\x05" b"S\x00"
|
|
673
|
-
)
|
|
671
|
+
expected = b"|\x05r\x02|\x00}\x05d\x01}\x05|\x05S\x00"
|
|
674
672
|
elif OFFSET_AS_INSTRUCTION:
|
|
675
673
|
# The argument of the jump is divided by 2
|
|
676
|
-
expected =
|
|
677
|
-
b"|\x05" b"r\x04" b"|\x00" b"}\x05" b"d\x01" b"}\x05" b"|\x05" b"S\x00"
|
|
678
|
-
)
|
|
674
|
+
expected = b"|\x05r\x04|\x00}\x05d\x01}\x05|\x05S\x00"
|
|
679
675
|
else:
|
|
680
|
-
expected =
|
|
681
|
-
b"|\x05" b"r\x08" b"|\x00" b"}\x05" b"d\x01" b"}\x05" b"|\x05" b"S\x00"
|
|
682
|
-
)
|
|
676
|
+
expected = b"|\x05r\x08|\x00}\x05d\x01}\x05|\x05S\x00"
|
|
683
677
|
|
|
684
678
|
code = bytecode.to_code()
|
|
685
679
|
self.assertEqual(code.co_consts, (None, 3))
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
2
|
import sys
|
|
3
3
|
import unittest
|
|
4
|
+
from copy import copy
|
|
4
5
|
|
|
5
6
|
from bytecode import (
|
|
6
7
|
Bytecode,
|
|
@@ -11,10 +12,52 @@ from bytecode import (
|
|
|
11
12
|
)
|
|
12
13
|
from bytecode.flags import infer_flags
|
|
13
14
|
from bytecode.instr import UNSET, FreeVar, Instr
|
|
15
|
+
from bytecode.utils import PY311, PY312
|
|
14
16
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
17
|
+
|
|
18
|
+
def trivial():
|
|
19
|
+
pass
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def _fact(a):
|
|
23
|
+
def inner():
|
|
24
|
+
return a
|
|
25
|
+
|
|
26
|
+
return inner
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
hasfree = _fact(1)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def gen():
|
|
33
|
+
yield 1
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
async def trivial_async():
|
|
37
|
+
pass
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
async def async_await(a):
|
|
41
|
+
await a
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
async def async_with_comprehension():
|
|
45
|
+
return [await i for i in range(10)]
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
async def async_generator():
|
|
49
|
+
yield 1
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
FLAG_INFERENCE_TEST_CASES = [
|
|
53
|
+
trivial,
|
|
54
|
+
hasfree,
|
|
55
|
+
gen,
|
|
56
|
+
trivial_async,
|
|
57
|
+
async_await,
|
|
58
|
+
async_with_comprehension,
|
|
59
|
+
async_generator,
|
|
60
|
+
]
|
|
18
61
|
|
|
19
62
|
|
|
20
63
|
class FlagsTests(unittest.TestCase):
|
|
@@ -58,16 +101,31 @@ class FlagsTests(unittest.TestCase):
|
|
|
58
101
|
self.assertFalse(bool(code.flags & CompilerFlags.OPTIMIZED))
|
|
59
102
|
self.assertFalse(bool(code.flags & CompilerFlags.NOFREE))
|
|
60
103
|
|
|
104
|
+
def test_function_rountrip(self):
|
|
105
|
+
for f in FLAG_INFERENCE_TEST_CASES:
|
|
106
|
+
for cls in (Bytecode, ConcreteBytecode):
|
|
107
|
+
with self.subTest(f"Testing {f.__name__} with {cls}"):
|
|
108
|
+
b = cls.from_code(f.__code__)
|
|
109
|
+
existing = copy(b.flags)
|
|
110
|
+
b.update_flags()
|
|
111
|
+
# NOTE: as far as I can tell NOFREE is not used by CPython anymore
|
|
112
|
+
# it shows up nowhere in the interpreter logic and only exist in
|
|
113
|
+
# dis and inspect...
|
|
114
|
+
self.assertEqual(
|
|
115
|
+
existing & ~CompilerFlags.NOFREE,
|
|
116
|
+
b.flags & ~CompilerFlags.NOFREE,
|
|
117
|
+
)
|
|
118
|
+
|
|
61
119
|
def test_async_gen_no_flag_is_async_None(self):
|
|
62
120
|
# Test inference in the absence of any flag set on the bytecode
|
|
63
121
|
|
|
64
122
|
# Infer generator
|
|
65
123
|
code = ConcreteBytecode()
|
|
66
124
|
code.append(
|
|
67
|
-
ConcreteInstr("YIELD_VALUE", 0)
|
|
68
|
-
if sys.version_info >= (3, 12)
|
|
69
|
-
else ConcreteInstr("YIELD_VALUE")
|
|
125
|
+
ConcreteInstr("YIELD_VALUE", 0) if PY312 else ConcreteInstr("YIELD_VALUE")
|
|
70
126
|
)
|
|
127
|
+
if PY311:
|
|
128
|
+
code.append(ConcreteInstr("RESUME", 1))
|
|
71
129
|
code.update_flags()
|
|
72
130
|
self.assertTrue(bool(code.flags & CompilerFlags.GENERATOR))
|
|
73
131
|
|
|
@@ -80,24 +138,22 @@ class FlagsTests(unittest.TestCase):
|
|
|
80
138
|
self.assertTrue(bool(code.flags & CompilerFlags.COROUTINE))
|
|
81
139
|
|
|
82
140
|
# Infer coroutine or async generator
|
|
83
|
-
for i, expected in (
|
|
84
|
-
("YIELD_VALUE", CompilerFlags.ASYNC_GENERATOR),
|
|
85
|
-
("
|
|
141
|
+
for i, r, expected in (
|
|
142
|
+
("YIELD_VALUE", 1, CompilerFlags.ASYNC_GENERATOR),
|
|
143
|
+
("YIELD_VALUE", 2, CompilerFlags.ASYNC_GENERATOR),
|
|
144
|
+
# YIELD_VALUE is used for normal await flow in Py 3.11+ when followed
|
|
145
|
+
# by a RESUME whose lowest two bits are set to 3
|
|
146
|
+
*((("YIELD_VALUE", 3, CompilerFlags.COROUTINE),) if PY311 else ()),
|
|
147
|
+
("YIELD_FROM", 0, CompilerFlags.COROUTINE),
|
|
86
148
|
):
|
|
87
149
|
with self.subTest(i):
|
|
88
|
-
if
|
|
150
|
+
if PY311 and i == "YIELD_FROM":
|
|
89
151
|
self.skipTest("YIELD_FROM does not exist on 3.11")
|
|
90
152
|
code = ConcreteBytecode()
|
|
91
|
-
code.append(
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
)
|
|
95
|
-
)
|
|
96
|
-
code.append(
|
|
97
|
-
ConcreteInstr(i, 0)
|
|
98
|
-
if sys.version_info >= (3, 12)
|
|
99
|
-
else ConcreteInstr(i)
|
|
100
|
-
)
|
|
153
|
+
code.append(ConcreteInstr("GET_AWAITABLE", 0 if PY311 else UNSET))
|
|
154
|
+
code.append(ConcreteInstr(i, 0) if PY312 else ConcreteInstr(i))
|
|
155
|
+
if PY311:
|
|
156
|
+
code.append(ConcreteInstr("RESUME", r))
|
|
101
157
|
code.update_flags()
|
|
102
158
|
self.assertTrue(bool(code.flags & expected))
|
|
103
159
|
|
|
@@ -110,21 +166,23 @@ class FlagsTests(unittest.TestCase):
|
|
|
110
166
|
self.assertTrue(bool(code.flags & CompilerFlags.COROUTINE))
|
|
111
167
|
|
|
112
168
|
# Infer coroutine or async generator
|
|
113
|
-
for i, expected in (
|
|
114
|
-
("YIELD_VALUE", CompilerFlags.ASYNC_GENERATOR),
|
|
115
|
-
("
|
|
169
|
+
for i, r, expected in (
|
|
170
|
+
("YIELD_VALUE", 1, CompilerFlags.ASYNC_GENERATOR),
|
|
171
|
+
("YIELD_VALUE", 2, CompilerFlags.ASYNC_GENERATOR),
|
|
172
|
+
# YIELD_VALUE is used for normal await flow in Py 3.11+ when followed
|
|
173
|
+
# by a RESUME whose lowest two bits are set to 3
|
|
174
|
+
*((("YIELD_VALUE", 3, CompilerFlags.COROUTINE),) if PY311 else ()),
|
|
175
|
+
("YIELD_FROM", 0, CompilerFlags.COROUTINE),
|
|
116
176
|
):
|
|
117
177
|
with self.subTest(i):
|
|
118
|
-
if
|
|
178
|
+
if PY311 and i == "YIELD_FROM":
|
|
119
179
|
self.skipTest("YIELD_FROM does not exist on 3.11")
|
|
120
180
|
code = ConcreteBytecode()
|
|
121
|
-
code.append(
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
else ConcreteInstr(i)
|
|
125
|
-
)
|
|
181
|
+
code.append(ConcreteInstr(i, 0) if PY312 else ConcreteInstr(i))
|
|
182
|
+
if PY311:
|
|
183
|
+
code.append(ConcreteInstr("RESUME", r))
|
|
126
184
|
code.update_flags(is_async=True)
|
|
127
|
-
self.
|
|
185
|
+
self.assertEqual(code.flags & expected, expected)
|
|
128
186
|
|
|
129
187
|
def test_async_gen_no_flag_is_async_False(self):
|
|
130
188
|
# Test inference when we request a non-async function
|
|
@@ -132,10 +190,10 @@ class FlagsTests(unittest.TestCase):
|
|
|
132
190
|
# Infer generator
|
|
133
191
|
code = ConcreteBytecode()
|
|
134
192
|
code.append(
|
|
135
|
-
ConcreteInstr("YIELD_VALUE", 0)
|
|
136
|
-
if sys.version_info >= (3, 12)
|
|
137
|
-
else ConcreteInstr("YIELD_VALUE")
|
|
193
|
+
ConcreteInstr("YIELD_VALUE", 0) if PY312 else ConcreteInstr("YIELD_VALUE")
|
|
138
194
|
)
|
|
195
|
+
if PY311:
|
|
196
|
+
code.append(ConcreteInstr("RESUME", 1))
|
|
139
197
|
code.flags = CompilerFlags(CompilerFlags.COROUTINE)
|
|
140
198
|
code.update_flags(is_async=False)
|
|
141
199
|
self.assertTrue(bool(code.flags & CompilerFlags.GENERATOR))
|
|
@@ -157,9 +215,11 @@ class FlagsTests(unittest.TestCase):
|
|
|
157
215
|
code = ConcreteBytecode()
|
|
158
216
|
code.append(
|
|
159
217
|
ConcreteInstr("YIELD_VALUE", 0)
|
|
160
|
-
if
|
|
218
|
+
if PY312
|
|
161
219
|
else ConcreteInstr("YIELD_VALUE")
|
|
162
220
|
)
|
|
221
|
+
if PY311:
|
|
222
|
+
code.append(ConcreteInstr("RESUME", 1))
|
|
163
223
|
for f, expected in (
|
|
164
224
|
(CompilerFlags.COROUTINE, CompilerFlags.ASYNC_GENERATOR),
|
|
165
225
|
(CompilerFlags.ASYNC_GENERATOR, CompilerFlags.ASYNC_GENERATOR),
|
|
@@ -170,7 +230,7 @@ class FlagsTests(unittest.TestCase):
|
|
|
170
230
|
self.assertTrue(bool(code.flags & expected))
|
|
171
231
|
|
|
172
232
|
# Infer coroutine
|
|
173
|
-
if
|
|
233
|
+
if not PY311:
|
|
174
234
|
code = ConcreteBytecode()
|
|
175
235
|
code.append(ConcreteInstr("YIELD_FROM"))
|
|
176
236
|
for f, expected in (
|
|
@@ -187,11 +247,7 @@ class FlagsTests(unittest.TestCase):
|
|
|
187
247
|
|
|
188
248
|
# Crash on ITERABLE_COROUTINE with async bytecode
|
|
189
249
|
code = ConcreteBytecode()
|
|
190
|
-
code.append(
|
|
191
|
-
ConcreteInstr(
|
|
192
|
-
"GET_AWAITABLE", 0 if sys.version_info >= (3, 11) else UNSET
|
|
193
|
-
)
|
|
194
|
-
)
|
|
250
|
+
code.append(ConcreteInstr("GET_AWAITABLE", 0 if PY311 else UNSET))
|
|
195
251
|
code.flags = CompilerFlags(CompilerFlags.ITERABLE_COROUTINE)
|
|
196
252
|
with self.assertRaises(ValueError):
|
|
197
253
|
code.update_flags(is_async=is_async)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
__all__ = [
|
|
2
|
-
"
|
|
3
|
-
"Instr",
|
|
4
|
-
"SetLineno",
|
|
2
|
+
"BinaryOp",
|
|
5
3
|
"Bytecode",
|
|
6
|
-
"
|
|
4
|
+
"Compare",
|
|
5
|
+
"CompilerFlags",
|
|
7
6
|
"ConcreteBytecode",
|
|
7
|
+
"ConcreteInstr",
|
|
8
8
|
"ControlFlowGraph",
|
|
9
|
-
"
|
|
10
|
-
"
|
|
11
|
-
"
|
|
9
|
+
"Instr",
|
|
10
|
+
"Label",
|
|
11
|
+
"SetLineno",
|
|
12
12
|
"__version__",
|
|
13
13
|
]
|
|
14
14
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|