bytecode 0.16.1__tar.gz → 0.17.0__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.1 → bytecode-0.17.0}/.github/workflows/cis.yml +4 -4
- {bytecode-0.16.1 → bytecode-0.17.0}/.github/workflows/docs.yml +1 -1
- {bytecode-0.16.1 → bytecode-0.17.0}/.github/workflows/frameworks.yml +2 -2
- {bytecode-0.16.1 → bytecode-0.17.0}/.github/workflows/release.yml +10 -11
- {bytecode-0.16.1/src/bytecode.egg-info → bytecode-0.17.0}/PKG-INFO +5 -4
- {bytecode-0.16.1 → bytecode-0.17.0}/doc/changelog.rst +34 -0
- {bytecode-0.16.1 → bytecode-0.17.0}/pyproject.toml +15 -3
- {bytecode-0.16.1 → bytecode-0.17.0}/src/bytecode/cfg.py +10 -5
- {bytecode-0.16.1 → bytecode-0.17.0}/src/bytecode/concrete.py +33 -4
- {bytecode-0.16.1 → bytecode-0.17.0}/src/bytecode/flags.py +2 -2
- {bytecode-0.16.1 → bytecode-0.17.0}/src/bytecode/instr.py +159 -15
- {bytecode-0.16.1 → bytecode-0.17.0}/src/bytecode/utils.py +1 -0
- {bytecode-0.16.1 → bytecode-0.17.0}/src/bytecode/version.py +2 -2
- {bytecode-0.16.1 → bytecode-0.17.0/src/bytecode.egg-info}/PKG-INFO +5 -4
- {bytecode-0.16.1 → bytecode-0.17.0}/tests/__init__.py +8 -3
- {bytecode-0.16.1 → bytecode-0.17.0}/tests/exception_handling_cases.py +18 -18
- {bytecode-0.16.1 → bytecode-0.17.0}/tests/frameworks/function.py +1 -1
- {bytecode-0.16.1 → bytecode-0.17.0}/tests/test_bytecode.py +124 -56
- {bytecode-0.16.1 → bytecode-0.17.0}/tests/test_cfg.py +71 -33
- {bytecode-0.16.1 → bytecode-0.17.0}/tests/test_concrete.py +112 -55
- {bytecode-0.16.1 → bytecode-0.17.0}/tests/test_instr.py +56 -7
- {bytecode-0.16.1 → bytecode-0.17.0}/tests/test_misc.py +182 -15
- {bytecode-0.16.1 → bytecode-0.17.0}/tox.ini +1 -1
- {bytecode-0.16.1 → bytecode-0.17.0}/.coveragerc +0 -0
- {bytecode-0.16.1 → bytecode-0.17.0}/.github/FUNDING.yml +0 -0
- {bytecode-0.16.1 → bytecode-0.17.0}/.github/dependabot.yml +0 -0
- {bytecode-0.16.1 → bytecode-0.17.0}/.gitignore +0 -0
- {bytecode-0.16.1 → bytecode-0.17.0}/.pre-commit-config.yaml +0 -0
- {bytecode-0.16.1 → bytecode-0.17.0}/.readthedocs.yaml +0 -0
- {bytecode-0.16.1 → bytecode-0.17.0}/COPYING +0 -0
- {bytecode-0.16.1 → bytecode-0.17.0}/MANIFEST.in +0 -0
- {bytecode-0.16.1 → bytecode-0.17.0}/README.rst +0 -0
- {bytecode-0.16.1 → bytecode-0.17.0}/TODO.rst +0 -0
- {bytecode-0.16.1 → bytecode-0.17.0}/codecov.yml +0 -0
- {bytecode-0.16.1 → bytecode-0.17.0}/doc/Makefile +0 -0
- {bytecode-0.16.1 → bytecode-0.17.0}/doc/api.rst +0 -0
- {bytecode-0.16.1 → bytecode-0.17.0}/doc/byteplay_codetransformer.rst +0 -0
- {bytecode-0.16.1 → bytecode-0.17.0}/doc/cfg.rst +0 -0
- {bytecode-0.16.1 → bytecode-0.17.0}/doc/conf.py +0 -0
- {bytecode-0.16.1 → bytecode-0.17.0}/doc/index.rst +0 -0
- {bytecode-0.16.1 → bytecode-0.17.0}/doc/make.bat +0 -0
- {bytecode-0.16.1 → bytecode-0.17.0}/doc/requirements.txt +0 -0
- {bytecode-0.16.1 → bytecode-0.17.0}/doc/todo.rst +0 -0
- {bytecode-0.16.1 → bytecode-0.17.0}/doc/usage.rst +0 -0
- {bytecode-0.16.1 → bytecode-0.17.0}/scripts/frameworks/boto3/run.sh +0 -0
- {bytecode-0.16.1 → bytecode-0.17.0}/scripts/frameworks/boto3/setup.sh +0 -0
- {bytecode-0.16.1 → bytecode-0.17.0}/setup.cfg +0 -0
- {bytecode-0.16.1 → bytecode-0.17.0}/src/bytecode/__init__.py +0 -0
- {bytecode-0.16.1 → bytecode-0.17.0}/src/bytecode/bytecode.py +0 -0
- {bytecode-0.16.1 → bytecode-0.17.0}/src/bytecode/py.typed +0 -0
- {bytecode-0.16.1 → bytecode-0.17.0}/src/bytecode.egg-info/SOURCES.txt +0 -0
- {bytecode-0.16.1 → bytecode-0.17.0}/src/bytecode.egg-info/dependency_links.txt +0 -0
- {bytecode-0.16.1 → bytecode-0.17.0}/src/bytecode.egg-info/requires.txt +0 -0
- {bytecode-0.16.1 → bytecode-0.17.0}/src/bytecode.egg-info/top_level.txt +0 -0
- {bytecode-0.16.1 → bytecode-0.17.0}/tests/cell_free_vars_cases.py +0 -0
- {bytecode-0.16.1 → bytecode-0.17.0}/tests/frameworks/module.py +0 -0
- {bytecode-0.16.1 → bytecode-0.17.0}/tests/frameworks/sitecustomize.py +0 -0
- {bytecode-0.16.1 → bytecode-0.17.0}/tests/long_lines_example.py +0 -0
- {bytecode-0.16.1 → bytecode-0.17.0}/tests/test_code.py +0 -0
- {bytecode-0.16.1 → bytecode-0.17.0}/tests/test_flags.py +0 -0
- {bytecode-0.16.1 → bytecode-0.17.0}/tests/util_annotation.py +0 -0
|
@@ -20,7 +20,7 @@ jobs:
|
|
|
20
20
|
name: Lint code
|
|
21
21
|
runs-on: ubuntu-latest
|
|
22
22
|
steps:
|
|
23
|
-
- uses: actions/checkout@
|
|
23
|
+
- uses: actions/checkout@v5
|
|
24
24
|
- name: Set up Python
|
|
25
25
|
uses: actions/setup-python@v5
|
|
26
26
|
with:
|
|
@@ -42,8 +42,6 @@ jobs:
|
|
|
42
42
|
fail-fast: false
|
|
43
43
|
matrix:
|
|
44
44
|
include:
|
|
45
|
-
- python-version: "3.8"
|
|
46
|
-
toxenv: py38
|
|
47
45
|
- python-version: "3.9"
|
|
48
46
|
toxenv: py39
|
|
49
47
|
- python-version: "3.10"
|
|
@@ -54,8 +52,10 @@ jobs:
|
|
|
54
52
|
toxenv: py312
|
|
55
53
|
- python-version: "3.13"
|
|
56
54
|
toxenv: py313
|
|
55
|
+
- python-version: "3.14-dev"
|
|
56
|
+
toxenv: py314
|
|
57
57
|
steps:
|
|
58
|
-
- uses: actions/checkout@
|
|
58
|
+
- uses: actions/checkout@v5
|
|
59
59
|
- name: Get history and tags for SCM versioning to work
|
|
60
60
|
run: |
|
|
61
61
|
git fetch --prune --unshallow
|
|
@@ -21,10 +21,10 @@ jobs:
|
|
|
21
21
|
strategy:
|
|
22
22
|
fail-fast: false
|
|
23
23
|
matrix:
|
|
24
|
-
python-version: ["3.
|
|
24
|
+
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
|
|
25
25
|
|
|
26
26
|
steps:
|
|
27
|
-
- uses: actions/checkout@
|
|
27
|
+
- uses: actions/checkout@v5
|
|
28
28
|
with:
|
|
29
29
|
fetch-depth: 0
|
|
30
30
|
|
|
@@ -13,7 +13,7 @@ jobs:
|
|
|
13
13
|
runs-on: ubuntu-latest
|
|
14
14
|
steps:
|
|
15
15
|
- name: Checkout
|
|
16
|
-
uses: actions/checkout@
|
|
16
|
+
uses: actions/checkout@v5
|
|
17
17
|
- name: Get history and tags for SCM versioning to work
|
|
18
18
|
run: |
|
|
19
19
|
git fetch --prune --unshallow
|
|
@@ -43,7 +43,7 @@ jobs:
|
|
|
43
43
|
runs-on: ubuntu-latest
|
|
44
44
|
steps:
|
|
45
45
|
- name: Checkout
|
|
46
|
-
uses: actions/checkout@
|
|
46
|
+
uses: actions/checkout@v5
|
|
47
47
|
- name: Get history and tags for SCM versioning to work
|
|
48
48
|
run: |
|
|
49
49
|
git fetch --prune --unshallow
|
|
@@ -72,20 +72,20 @@ jobs:
|
|
|
72
72
|
if: github.event_name == 'push'
|
|
73
73
|
needs: [build_wheel, build_sdist]
|
|
74
74
|
runs-on: ubuntu-latest
|
|
75
|
+
environment:
|
|
76
|
+
name: pypi
|
|
77
|
+
url: https://pypi.org/p/kiwisolver
|
|
78
|
+
permissions:
|
|
79
|
+
id-token: write
|
|
75
80
|
steps:
|
|
76
81
|
- name: Download all the dists
|
|
77
|
-
uses: actions/download-artifact@
|
|
82
|
+
uses: actions/download-artifact@v5.0.0
|
|
78
83
|
with:
|
|
79
84
|
pattern: cibw-*
|
|
80
85
|
path: dist
|
|
81
86
|
merge-multiple: true
|
|
82
87
|
|
|
83
88
|
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
84
|
-
with:
|
|
85
|
-
user: __token__
|
|
86
|
-
password: ${{ secrets.pypi_password }}
|
|
87
|
-
# To test:
|
|
88
|
-
# repository_url: https://test.pypi.org/legacy/
|
|
89
89
|
|
|
90
90
|
github-release:
|
|
91
91
|
name: >-
|
|
@@ -101,15 +101,14 @@ jobs:
|
|
|
101
101
|
|
|
102
102
|
steps:
|
|
103
103
|
- name: Download all the dists
|
|
104
|
-
uses: actions/download-artifact@
|
|
104
|
+
uses: actions/download-artifact@v5.0.0
|
|
105
105
|
with:
|
|
106
106
|
pattern: cibw-*
|
|
107
107
|
path: dist
|
|
108
108
|
merge-multiple: true
|
|
109
109
|
- name: Sign the dists with Sigstore
|
|
110
|
-
uses: sigstore/gh-action-sigstore-python@
|
|
110
|
+
uses: sigstore/gh-action-sigstore-python@v3.0.1
|
|
111
111
|
with:
|
|
112
|
-
password: ${{ secrets.pypi_password }}
|
|
113
112
|
inputs: >-
|
|
114
113
|
./dist/*.tar.gz
|
|
115
114
|
./dist/*.whl
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: bytecode
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.17.0
|
|
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>
|
|
@@ -36,17 +36,18 @@ Classifier: License :: OSI Approved :: MIT License
|
|
|
36
36
|
Classifier: Natural Language :: English
|
|
37
37
|
Classifier: Operating System :: OS Independent
|
|
38
38
|
Classifier: Programming Language :: Python :: 3
|
|
39
|
-
Classifier: Programming Language :: Python :: 3.8
|
|
40
39
|
Classifier: Programming Language :: Python :: 3.9
|
|
41
40
|
Classifier: Programming Language :: Python :: 3.10
|
|
42
41
|
Classifier: Programming Language :: Python :: 3.11
|
|
43
42
|
Classifier: Programming Language :: Python :: 3.12
|
|
44
43
|
Classifier: Programming Language :: Python :: 3.13
|
|
44
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
45
45
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
46
|
-
Requires-Python: >=3.
|
|
46
|
+
Requires-Python: >=3.9
|
|
47
47
|
Description-Content-Type: text/x-rst
|
|
48
48
|
License-File: COPYING
|
|
49
49
|
Requires-Dist: typing_extensions; python_version < "3.10"
|
|
50
|
+
Dynamic: license-file
|
|
50
51
|
|
|
51
52
|
********
|
|
52
53
|
bytecode
|
|
@@ -1,6 +1,40 @@
|
|
|
1
1
|
ChangeLog
|
|
2
2
|
=========
|
|
3
3
|
|
|
4
|
+
03-09-2025: Version 0.17.0
|
|
5
|
+
--------------------------
|
|
6
|
+
|
|
7
|
+
New features:
|
|
8
|
+
|
|
9
|
+
- Add support for Python 3.14 PR #166
|
|
10
|
+
|
|
11
|
+
Support for Python 3.14, comes with a number of changes reflecting changes in
|
|
12
|
+
CPython bytecode itself:
|
|
13
|
+
|
|
14
|
+
- introduced an enum for BINARY_OP argument which now supports subscribe.
|
|
15
|
+
When disassembling the enum is always used, when creating bytecode from
|
|
16
|
+
scratch integer values are coerced into the right enum member.
|
|
17
|
+
- support BUILD_TEMPLATE, BUILD_INTERPOLATION, LOAD_SMALL_INT, LOAD_FAST_BORROW
|
|
18
|
+
and LOAD_FAST_BORROW_LOAD_FAST_BORROW
|
|
19
|
+
- LOAD_COMMON_CONSTANT, LOAD_SPECIAL whose argument is described using dedicated
|
|
20
|
+
enums CommonConstant, SpecialMethod
|
|
21
|
+
- CONVERT_VALUE (FORMAT_VALUE in Python < 3.13) now use the FormatValue enum.
|
|
22
|
+
When disassembling the enum is always used, when creating bytecode from
|
|
23
|
+
scratch integer values are coerced into the right enum member.
|
|
24
|
+
|
|
25
|
+
Bugfixes:
|
|
26
|
+
|
|
27
|
+
- properly set the next_block attribute of the new block created by
|
|
28
|
+
ControlFlowGraph.split_block. PR #170
|
|
29
|
+
|
|
30
|
+
2025-04-14: Version 0.16.2
|
|
31
|
+
--------------------------
|
|
32
|
+
|
|
33
|
+
Bugfixes:
|
|
34
|
+
|
|
35
|
+
- fix ControlFlowGraph dead block detection by accounting for fall-through
|
|
36
|
+
edges. PR #161
|
|
37
|
+
|
|
4
38
|
2025-01-21: Version 0.16.1
|
|
5
39
|
--------------------------
|
|
6
40
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
name = "bytecode"
|
|
3
3
|
description = "Python module to generate and modify bytecode"
|
|
4
4
|
readme = "README.rst"
|
|
5
|
-
requires-python = ">=3.
|
|
5
|
+
requires-python = ">=3.9"
|
|
6
6
|
license = { file = "COPYING" }
|
|
7
7
|
authors = [{ name = "Victor Stinner", email = "victor.stinner@gmail.com" }]
|
|
8
8
|
maintainers = [{ name = "Matthieu C. Dartiailh", email = "m.dartiailh@gmail.com" }]
|
|
@@ -13,12 +13,12 @@
|
|
|
13
13
|
"Natural Language :: English",
|
|
14
14
|
"Operating System :: OS Independent",
|
|
15
15
|
"Programming Language :: Python :: 3",
|
|
16
|
-
"Programming Language :: Python :: 3.8",
|
|
17
16
|
"Programming Language :: Python :: 3.9",
|
|
18
17
|
"Programming Language :: Python :: 3.10",
|
|
19
18
|
"Programming Language :: Python :: 3.11",
|
|
20
19
|
"Programming Language :: Python :: 3.12",
|
|
21
20
|
"Programming Language :: Python :: 3.13",
|
|
21
|
+
"Programming Language :: Python :: 3.14",
|
|
22
22
|
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
23
23
|
]
|
|
24
24
|
dependencies = ["typing_extensions;python_version<'3.10'"]
|
|
@@ -36,6 +36,18 @@
|
|
|
36
36
|
requires = ["setuptools>=61.2", "wheel", "setuptools_scm[toml]>=3.4.3"]
|
|
37
37
|
build-backend = "setuptools.build_meta"
|
|
38
38
|
|
|
39
|
+
[dependency-groups]
|
|
40
|
+
dev = [
|
|
41
|
+
"mypy>=1.16.1",
|
|
42
|
+
"pytest>=8",
|
|
43
|
+
"pytest-cov>=6",
|
|
44
|
+
"ruff>=0.12.0",
|
|
45
|
+
]
|
|
46
|
+
test = [
|
|
47
|
+
"pytest>=8",
|
|
48
|
+
"pytest-cov",
|
|
49
|
+
]
|
|
50
|
+
|
|
39
51
|
[tool.setuptools_scm]
|
|
40
52
|
write_to = "src/bytecode/version.py"
|
|
41
53
|
write_to_template = """
|
|
@@ -74,7 +86,7 @@ __version__ = "{version}"
|
|
|
74
86
|
extra-standard-library = ["opcode"]
|
|
75
87
|
|
|
76
88
|
[tool.ruff.lint.mccabe]
|
|
77
|
-
max-complexity =
|
|
89
|
+
max-complexity = 43
|
|
78
90
|
|
|
79
91
|
[tool.mypy]
|
|
80
92
|
follow_imports = "normal"
|
|
@@ -710,6 +710,7 @@ class ControlFlowGraph(_bytecode.BaseBytecode):
|
|
|
710
710
|
del block[index:]
|
|
711
711
|
|
|
712
712
|
block2 = BasicBlock(instructions)
|
|
713
|
+
block2.next_block = block.next_block
|
|
713
714
|
block.next_block = block2
|
|
714
715
|
|
|
715
716
|
for block in self[block_index + 1 :]:
|
|
@@ -731,12 +732,18 @@ class ControlFlowGraph(_bytecode.BaseBytecode):
|
|
|
731
732
|
if id(block) in seen_block_ids:
|
|
732
733
|
continue
|
|
733
734
|
seen_block_ids.add(id(block))
|
|
735
|
+
fall_through = True
|
|
734
736
|
for i in block:
|
|
735
|
-
if isinstance(i, Instr)
|
|
736
|
-
|
|
737
|
+
if isinstance(i, Instr):
|
|
738
|
+
if isinstance(i.arg, BasicBlock):
|
|
739
|
+
stack.append(i.arg)
|
|
740
|
+
if i.is_final():
|
|
741
|
+
fall_through = False
|
|
737
742
|
elif isinstance(i, TryBegin):
|
|
738
743
|
assert isinstance(i.target, BasicBlock)
|
|
739
744
|
stack.append(i.target)
|
|
745
|
+
if fall_through and block.next_block:
|
|
746
|
+
stack.append(block.next_block)
|
|
740
747
|
|
|
741
748
|
return [b for b in self if id(b) not in seen_block_ids]
|
|
742
749
|
|
|
@@ -865,9 +872,7 @@ class ControlFlowGraph(_bytecode.BaseBytecode):
|
|
|
865
872
|
# the new one since the blocks are disconnected.
|
|
866
873
|
if last_instr.is_final() and temp:
|
|
867
874
|
old_block.append(TryEnd(try_begins[active_try_begin][-1]))
|
|
868
|
-
new_tb =
|
|
869
|
-
active_try_begin.target, active_try_begin.push_lasti
|
|
870
|
-
)
|
|
875
|
+
new_tb = active_try_begin.copy()
|
|
871
876
|
block.append(new_tb)
|
|
872
877
|
# Add this new TryBegin to the map to properly update
|
|
873
878
|
# the target.
|
|
@@ -26,18 +26,25 @@ import bytecode as _bytecode
|
|
|
26
26
|
from bytecode.flags import CompilerFlags
|
|
27
27
|
from bytecode.instr import (
|
|
28
28
|
_UNSET,
|
|
29
|
+
BINARY_OPS,
|
|
29
30
|
BITFLAG2_OPCODES,
|
|
30
31
|
BITFLAG_OPCODES,
|
|
32
|
+
COMMON_CONSTANT_OPS,
|
|
31
33
|
DUAL_ARG_OPCODES,
|
|
32
34
|
DUAL_ARG_OPCODES_SINGLE_OPS,
|
|
35
|
+
FORMAT_VALUE_OPS,
|
|
33
36
|
INTRINSIC,
|
|
34
37
|
INTRINSIC_1OP,
|
|
35
38
|
INTRINSIC_2OP,
|
|
36
39
|
PLACEHOLDER_LABEL,
|
|
40
|
+
SPECIAL_OPS,
|
|
37
41
|
UNSET,
|
|
38
42
|
BaseInstr,
|
|
43
|
+
BinaryOp,
|
|
39
44
|
CellVar,
|
|
45
|
+
CommonConstant,
|
|
40
46
|
Compare,
|
|
47
|
+
FormatValue,
|
|
41
48
|
FreeVar,
|
|
42
49
|
Instr,
|
|
43
50
|
InstrArg,
|
|
@@ -46,6 +53,7 @@ from bytecode.instr import (
|
|
|
46
53
|
Intrinsic2Op,
|
|
47
54
|
Label,
|
|
48
55
|
SetLineno,
|
|
56
|
+
SpecialMethod,
|
|
49
57
|
TryBegin,
|
|
50
58
|
TryEnd,
|
|
51
59
|
_check_arg_int,
|
|
@@ -1056,7 +1064,10 @@ class ConcreteBytecode(_bytecode._BaseBytecodeList[Union[ConcreteInstr, SetLinen
|
|
|
1056
1064
|
arg = locals_lookup[c_arg]
|
|
1057
1065
|
elif opcode in _opcode.hasname:
|
|
1058
1066
|
if opcode in BITFLAG_OPCODES:
|
|
1059
|
-
arg = (
|
|
1067
|
+
arg = (
|
|
1068
|
+
bool(c_arg & 1),
|
|
1069
|
+
self.names[c_arg >> 1],
|
|
1070
|
+
)
|
|
1060
1071
|
elif opcode in BITFLAG2_OPCODES:
|
|
1061
1072
|
arg = (bool(c_arg & 1), bool(c_arg & 2), self.names[c_arg >> 2])
|
|
1062
1073
|
else:
|
|
@@ -1082,6 +1093,20 @@ class ConcreteBytecode(_bytecode._BaseBytecodeList[Union[ConcreteInstr, SetLinen
|
|
|
1082
1093
|
arg = Intrinsic1Op(c_arg)
|
|
1083
1094
|
elif opcode in INTRINSIC_2OP:
|
|
1084
1095
|
arg = Intrinsic2Op(c_arg)
|
|
1096
|
+
elif opcode in BINARY_OPS:
|
|
1097
|
+
arg = BinaryOp(c_arg)
|
|
1098
|
+
elif opcode in COMMON_CONSTANT_OPS:
|
|
1099
|
+
arg = CommonConstant(c_arg)
|
|
1100
|
+
elif opcode in SPECIAL_OPS:
|
|
1101
|
+
arg = SpecialMethod(c_arg)
|
|
1102
|
+
elif opcode in FORMAT_VALUE_OPS:
|
|
1103
|
+
if opcode in BITFLAG_OPCODES:
|
|
1104
|
+
arg = (
|
|
1105
|
+
bool(c_arg & 1),
|
|
1106
|
+
FormatValue(c_arg >> 1),
|
|
1107
|
+
)
|
|
1108
|
+
else:
|
|
1109
|
+
arg = FormatValue(c_arg)
|
|
1085
1110
|
else:
|
|
1086
1111
|
arg = c_arg
|
|
1087
1112
|
|
|
@@ -1143,7 +1168,7 @@ class ConcreteBytecode(_bytecode._BaseBytecodeList[Union[ConcreteInstr, SetLinen
|
|
|
1143
1168
|
|
|
1144
1169
|
|
|
1145
1170
|
class _ConvertBytecodeToConcrete:
|
|
1146
|
-
#
|
|
1171
|
+
# FIXME document attributes
|
|
1147
1172
|
|
|
1148
1173
|
#: Default number of passes of compute_jumps() before giving up. Refer to
|
|
1149
1174
|
#: assemble_jump_offsets() in compile.c for background.
|
|
@@ -1316,9 +1341,13 @@ class _ConvertBytecodeToConcrete:
|
|
|
1316
1341
|
isinstance(arg, tuple)
|
|
1317
1342
|
and len(arg) == 2
|
|
1318
1343
|
and isinstance(arg[0], bool)
|
|
1319
|
-
and isinstance(arg[1], str)
|
|
1320
1344
|
), arg
|
|
1321
|
-
|
|
1345
|
+
if isinstance(arg[1], str):
|
|
1346
|
+
index = self.add(self.names, arg[1])
|
|
1347
|
+
elif isinstance(arg, FormatValue):
|
|
1348
|
+
index = int(arg)
|
|
1349
|
+
else:
|
|
1350
|
+
assert False, arg # noqa
|
|
1322
1351
|
c_arg = int(arg[0]) + (index << 1)
|
|
1323
1352
|
elif opcode in BITFLAG2_OPCODES:
|
|
1324
1353
|
assert (
|
|
@@ -6,7 +6,7 @@ from typing import Optional, Union
|
|
|
6
6
|
import bytecode as _bytecode
|
|
7
7
|
|
|
8
8
|
from .instr import DUAL_ARG_OPCODES, CellVar, FreeVar
|
|
9
|
-
from .utils import PY311, PY312, PY313
|
|
9
|
+
from .utils import PY311, PY312, PY313, PY314
|
|
10
10
|
|
|
11
11
|
|
|
12
12
|
class CompilerFlags(IntFlag):
|
|
@@ -48,7 +48,7 @@ ASYNC_OPCODES = (
|
|
|
48
48
|
_opcode.opmap["GET_AWAITABLE"],
|
|
49
49
|
_opcode.opmap["GET_AITER"],
|
|
50
50
|
_opcode.opmap["GET_ANEXT"],
|
|
51
|
-
_opcode.opmap["BEFORE_ASYNC_WITH"],
|
|
51
|
+
*((_opcode.opmap["BEFORE_ASYNC_WITH"],) if not PY314 else ()), # Removed in 3.14+
|
|
52
52
|
*((_opcode.opmap["SETUP_ASYNC_WITH"],) if not PY311 else ()), # Removed in 3.11+
|
|
53
53
|
_opcode.opmap["END_ASYNC_FOR"],
|
|
54
54
|
*((_opcode.opmap["ASYNC_GEN_WRAP"],) if PY311 and not PY312 else ()), # New in 3.11
|
|
@@ -13,7 +13,7 @@ except ImportError:
|
|
|
13
13
|
from typing_extensions import TypeGuard # type: ignore
|
|
14
14
|
|
|
15
15
|
import bytecode as _bytecode
|
|
16
|
-
from bytecode.utils import PY311, PY312, PY313
|
|
16
|
+
from bytecode.utils import PY311, PY312, PY313, PY314
|
|
17
17
|
|
|
18
18
|
# --- Instruction argument tools and
|
|
19
19
|
|
|
@@ -22,20 +22,48 @@ MIN_INSTRUMENTED_OPCODE = getattr(_opcode, "MIN_INSTRUMENTED_OPCODE", 256)
|
|
|
22
22
|
# Instructions relying on a bit to modify its behavior.
|
|
23
23
|
# The lowest bit is used to encode custom behavior.
|
|
24
24
|
BITFLAG_OPCODES = (
|
|
25
|
-
(
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
25
|
+
(
|
|
26
|
+
_opcode.opmap["BUILD_INTERPOLATION"],
|
|
27
|
+
_opcode.opmap["LOAD_GLOBAL"],
|
|
28
|
+
_opcode.opmap["LOAD_ATTR"],
|
|
29
|
+
)
|
|
30
|
+
if PY314
|
|
31
|
+
else (
|
|
32
|
+
(_opcode.opmap["LOAD_GLOBAL"], _opcode.opmap["LOAD_ATTR"])
|
|
33
|
+
if PY312
|
|
34
|
+
else ((_opcode.opmap["LOAD_GLOBAL"],) if PY311 else ())
|
|
35
|
+
)
|
|
30
36
|
)
|
|
31
37
|
|
|
32
38
|
BITFLAG2_OPCODES = (_opcode.opmap["LOAD_SUPER_ATTR"],) if PY312 else ()
|
|
33
39
|
|
|
40
|
+
# Binary op opcode which has a dedicated arg
|
|
41
|
+
BINARY_OPS = (_opcode.opmap["BINARY_OP"],) if PY311 else ()
|
|
42
|
+
|
|
34
43
|
# Intrinsic related opcodes
|
|
35
44
|
INTRINSIC_1OP = (_opcode.opmap["CALL_INTRINSIC_1"],) if PY312 else ()
|
|
36
45
|
INTRINSIC_2OP = (_opcode.opmap["CALL_INTRINSIC_2"],) if PY312 else ()
|
|
37
46
|
INTRINSIC = INTRINSIC_1OP + INTRINSIC_2OP
|
|
38
47
|
|
|
48
|
+
# Small integer related opcode
|
|
49
|
+
SMALL_INT_OPS = (_opcode.opmap["LOAD_SMALL_INT"],) if PY314 else ()
|
|
50
|
+
|
|
51
|
+
# Special method loading related opcodes
|
|
52
|
+
SPECIAL_OPS = (_opcode.opmap["LOAD_SPECIAL"],) if PY314 else ()
|
|
53
|
+
|
|
54
|
+
# Common constant loading related opcodes
|
|
55
|
+
COMMON_CONSTANT_OPS = (_opcode.opmap["LOAD_COMMON_CONSTANT"],) if PY314 else ()
|
|
56
|
+
|
|
57
|
+
# Value formatting related opcodes (only handle CONVERT_VALUE and BUILD_INTERPOLATION)
|
|
58
|
+
FORMAT_VALUE_OPS = (
|
|
59
|
+
(
|
|
60
|
+
_opcode.opmap["CONVERT_VALUE"],
|
|
61
|
+
_opcode.opmap["BUILD_INTERPOLATION"],
|
|
62
|
+
)
|
|
63
|
+
if PY314
|
|
64
|
+
else ((_opcode.opmap["CONVERT_VALUE"],) if PY313 else ())
|
|
65
|
+
)
|
|
66
|
+
|
|
39
67
|
HASJABS = () if PY313 else _opcode.hasjabs
|
|
40
68
|
if sys.version_info >= (3, 13):
|
|
41
69
|
HASJREL = _opcode.hasjump
|
|
@@ -51,6 +79,11 @@ if PY313:
|
|
|
51
79
|
_opcode.opmap["STORE_FAST_LOAD_FAST"],
|
|
52
80
|
_opcode.opmap["STORE_FAST_STORE_FAST"],
|
|
53
81
|
)
|
|
82
|
+
if PY314:
|
|
83
|
+
DUAL_ARG_OPCODES = (
|
|
84
|
+
*DUAL_ARG_OPCODES,
|
|
85
|
+
_opcode.opmap["LOAD_FAST_BORROW_LOAD_FAST_BORROW"],
|
|
86
|
+
)
|
|
54
87
|
DUAL_ARG_OPCODES_SINGLE_OPS = {
|
|
55
88
|
_opcode.opmap["LOAD_FAST_LOAD_FAST"]: ("LOAD_FAST", "LOAD_FAST"),
|
|
56
89
|
_opcode.opmap["STORE_FAST_LOAD_FAST"]: ("STORE_FAST", "LOAD_FAST"),
|
|
@@ -129,6 +162,8 @@ class BinaryOp(enum.IntEnum):
|
|
|
129
162
|
INPLACE_SUBTRACT = 23
|
|
130
163
|
INPLACE_TRUE_DIVIDE = 24
|
|
131
164
|
INPLACE_XOR = 25
|
|
165
|
+
if PY314:
|
|
166
|
+
SUBSCR = 26
|
|
132
167
|
|
|
133
168
|
|
|
134
169
|
@enum.unique
|
|
@@ -156,6 +191,34 @@ class Intrinsic2Op(enum.IntEnum):
|
|
|
156
191
|
INTRINSIC_SET_FUNCTION_TYPE_PARAMS = 4
|
|
157
192
|
|
|
158
193
|
|
|
194
|
+
@enum.unique
|
|
195
|
+
class FormatValue(enum.IntEnum):
|
|
196
|
+
STR = 1
|
|
197
|
+
REPR = 2
|
|
198
|
+
ASCII = 3
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
@enum.unique
|
|
202
|
+
class SpecialMethod(enum.IntEnum):
|
|
203
|
+
"""Special method names used with LOAD_SPECIAL"""
|
|
204
|
+
|
|
205
|
+
ENTER = 0
|
|
206
|
+
EXIT = 1
|
|
207
|
+
AENTER = 2
|
|
208
|
+
AEXIT = 3
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
@enum.unique
|
|
212
|
+
class CommonConstant(enum.IntEnum):
|
|
213
|
+
"""Common constants names used with LOAD_COMMON_CONSTANT"""
|
|
214
|
+
|
|
215
|
+
ASSERTION_ERROR = 0
|
|
216
|
+
NOT_IMPLEMENTED_ERROR = 1
|
|
217
|
+
BUILTIN_TUPLE = 2
|
|
218
|
+
BUILTIN_ALL = 3
|
|
219
|
+
BUILTIN_ANY = 4
|
|
220
|
+
|
|
221
|
+
|
|
159
222
|
# This make type checking happy but means it won't catch attempt to manipulate an unset
|
|
160
223
|
# statically. We would need guard on object attribute narrowed down through methods
|
|
161
224
|
class _UNSET(int):
|
|
@@ -336,15 +399,15 @@ STATIC_STACK_EFFECTS: Dict[str, Tuple[int, int]] = {
|
|
|
336
399
|
"CHECK_EXC_MATCH": (-2, 2), # (TOS1, TOS) -> (TOS1, bool)
|
|
337
400
|
"CHECK_EG_MATCH": (-2, 2), # (TOS, TOS1) -> non-matched, matched or TOS1, None)
|
|
338
401
|
"PREP_RERAISE_STAR": (-2, 1), # (TOS1, TOS) -> new exception group)
|
|
339
|
-
**
|
|
340
|
-
**
|
|
341
|
-
|
|
342
|
-
for k in (
|
|
402
|
+
**dict.fromkeys((o for o in _opcode.opmap if o.startswith("UNARY_")), (-1, 1)),
|
|
403
|
+
**dict.fromkeys(
|
|
404
|
+
(
|
|
343
405
|
o
|
|
344
406
|
for o in _opcode.opmap
|
|
345
|
-
if
|
|
346
|
-
)
|
|
347
|
-
|
|
407
|
+
if o.startswith("BINARY_") or o.startswith("INPLACE_")
|
|
408
|
+
),
|
|
409
|
+
(-2, 1),
|
|
410
|
+
),
|
|
348
411
|
# Python 3.12 changes not covered by dis.stack_effect
|
|
349
412
|
"BINARY_SLICE": (-3, 1),
|
|
350
413
|
# "STORE_SLICE" handled by dis.stack_effect
|
|
@@ -357,6 +420,7 @@ STATIC_STACK_EFFECTS: Dict[str, Tuple[int, int]] = {
|
|
|
357
420
|
"FORMAT_SIMPLE": (-1, 1), # new in 3.13
|
|
358
421
|
"FORMAT_SPEC": (-2, 1), # new in 3.13
|
|
359
422
|
"TO_BOOL": (-1, 1), # new in 3.13
|
|
423
|
+
"BUILD_TEMPLATE": (-2, 1), # new in 3.14
|
|
360
424
|
}
|
|
361
425
|
|
|
362
426
|
|
|
@@ -385,6 +449,7 @@ DYNAMIC_STACK_EFFECTS: Dict[
|
|
|
385
449
|
"FORMAT_VALUE": lambda effect, arg, jump: (effect - 1, 1),
|
|
386
450
|
# FOR_ITER needs TOS to be an iterator, hence a prerequisite of 1 on the stack
|
|
387
451
|
"FOR_ITER": lambda effect, arg, jump: (effect, 0) if jump else (-1, 2),
|
|
452
|
+
"BUILD_INTERPOLATION": lambda effect, arg, jump: (-(2 + (arg & 1)), 1),
|
|
388
453
|
**{
|
|
389
454
|
# Instr(UNPACK_* , n) pops 1 and pushes n
|
|
390
455
|
k: lambda effect, arg, jump: (-1, effect + 1)
|
|
@@ -660,6 +725,7 @@ class BaseInstr(Generic[A]):
|
|
|
660
725
|
arg = None
|
|
661
726
|
# 3.11 where LOAD_GLOBAL arg encode whether or we push a null
|
|
662
727
|
# 3.12 does the same for LOAD_ATTR
|
|
728
|
+
# 3.14 does this for BUILD_INTERPOLATION
|
|
663
729
|
elif self._opcode in BITFLAG_OPCODES and isinstance(self._arg, tuple):
|
|
664
730
|
assert len(self._arg) == 2
|
|
665
731
|
arg = self._arg[0]
|
|
@@ -802,8 +868,15 @@ InstrArg = Union[
|
|
|
802
868
|
FreeVar,
|
|
803
869
|
"_bytecode.BasicBlock",
|
|
804
870
|
Compare,
|
|
871
|
+
FormatValue,
|
|
872
|
+
BinaryOp,
|
|
873
|
+
Intrinsic1Op,
|
|
874
|
+
Intrinsic2Op,
|
|
875
|
+
CommonConstant,
|
|
876
|
+
SpecialMethod,
|
|
805
877
|
Tuple[bool, str],
|
|
806
878
|
Tuple[bool, bool, str],
|
|
879
|
+
Tuple[bool, FormatValue],
|
|
807
880
|
Tuple[Union[str, CellVar, FreeVar], Union[str, CellVar, FreeVar]],
|
|
808
881
|
]
|
|
809
882
|
|
|
@@ -817,7 +890,7 @@ class Instr(BaseInstr[InstrArg]):
|
|
|
817
890
|
arg = const_key(arg)
|
|
818
891
|
return (self._location, self._name, arg)
|
|
819
892
|
|
|
820
|
-
def _check_arg(self, name: str, opcode: int, arg: InstrArg) -> None:
|
|
893
|
+
def _check_arg(self, name: str, opcode: int, arg: InstrArg) -> None: # noqa: C901
|
|
821
894
|
if name == "EXTENDED_ARG":
|
|
822
895
|
raise ValueError(
|
|
823
896
|
"only concrete instruction can contain EXTENDED_ARG, "
|
|
@@ -854,7 +927,7 @@ class Instr(BaseInstr[InstrArg]):
|
|
|
854
927
|
and isinstance(arg[1], str)
|
|
855
928
|
):
|
|
856
929
|
raise TypeError(
|
|
857
|
-
"operation %s argument must be a tuple[bool, str], "
|
|
930
|
+
"operation %s argument must be a tuple[bool, str | FormatValue], "
|
|
858
931
|
"got %s (value=%s)" % (name, type(arg).__name__, str(arg))
|
|
859
932
|
)
|
|
860
933
|
|
|
@@ -910,6 +983,37 @@ class Instr(BaseInstr[InstrArg]):
|
|
|
910
983
|
"Compare, got %s" % (name, type(arg).__name__)
|
|
911
984
|
)
|
|
912
985
|
|
|
986
|
+
elif opcode in BINARY_OPS:
|
|
987
|
+
if not isinstance(arg, BinaryOp):
|
|
988
|
+
if isinstance(arg, int):
|
|
989
|
+
try:
|
|
990
|
+
arg = BinaryOp(arg)
|
|
991
|
+
except Exception as e:
|
|
992
|
+
raise TypeError(
|
|
993
|
+
"operation %s argument type must be "
|
|
994
|
+
"coercible to BinaryOp, got %s" % (name, type(arg).__name__)
|
|
995
|
+
) from e
|
|
996
|
+
else:
|
|
997
|
+
raise TypeError(
|
|
998
|
+
"operation %s argument type must be "
|
|
999
|
+
"BinaryOp, got %s" % (name, type(arg).__name__)
|
|
1000
|
+
)
|
|
1001
|
+
|
|
1002
|
+
# We do not enforce constant immortality since which constants are
|
|
1003
|
+
# immortal may differ between recompilation and execution.
|
|
1004
|
+
|
|
1005
|
+
elif opcode in SMALL_INT_OPS:
|
|
1006
|
+
if not isinstance(arg, int):
|
|
1007
|
+
raise TypeError(
|
|
1008
|
+
"operation %s argument type must be "
|
|
1009
|
+
"int, got %s" % (name, type(arg).__name__)
|
|
1010
|
+
)
|
|
1011
|
+
if arg < 0 or arg > 255:
|
|
1012
|
+
raise ValueError(
|
|
1013
|
+
"operation %s argument type must be an "
|
|
1014
|
+
"int between 0 and 255, got %s" % (name, arg)
|
|
1015
|
+
)
|
|
1016
|
+
|
|
913
1017
|
elif opcode in INTRINSIC_1OP:
|
|
914
1018
|
if not isinstance(arg, Intrinsic1Op):
|
|
915
1019
|
raise TypeError(
|
|
@@ -924,5 +1028,45 @@ class Instr(BaseInstr[InstrArg]):
|
|
|
924
1028
|
"Intrinsic2Op, got %s" % (name, type(arg).__name__)
|
|
925
1029
|
)
|
|
926
1030
|
|
|
1031
|
+
elif opcode in SPECIAL_OPS:
|
|
1032
|
+
if not isinstance(arg, SpecialMethod):
|
|
1033
|
+
raise TypeError(
|
|
1034
|
+
"operation %s argument type must be "
|
|
1035
|
+
"SpecialMethod, got %s" % (name, type(arg).__name__)
|
|
1036
|
+
)
|
|
1037
|
+
elif opcode in COMMON_CONSTANT_OPS:
|
|
1038
|
+
if not isinstance(arg, CommonConstant):
|
|
1039
|
+
raise TypeError(
|
|
1040
|
+
"operation %s argument type must be "
|
|
1041
|
+
"CommonConstant, got %s" % (name, type(arg).__name__)
|
|
1042
|
+
)
|
|
1043
|
+
|
|
1044
|
+
elif opcode in FORMAT_VALUE_OPS:
|
|
1045
|
+
if opcode in BITFLAG_OPCODES:
|
|
1046
|
+
if not (
|
|
1047
|
+
isinstance(arg, tuple)
|
|
1048
|
+
and len(arg) == 2
|
|
1049
|
+
and isinstance(arg[0], bool)
|
|
1050
|
+
and isinstance(arg[1], FormatValue)
|
|
1051
|
+
):
|
|
1052
|
+
raise TypeError(
|
|
1053
|
+
"operation %s argument must be a tuple[bool, FormatValue], "
|
|
1054
|
+
"got %s (value=%s)" % (name, type(arg).__name__, str(arg))
|
|
1055
|
+
)
|
|
1056
|
+
elif not isinstance(arg, FormatValue):
|
|
1057
|
+
if isinstance(arg, int):
|
|
1058
|
+
try:
|
|
1059
|
+
arg = FormatValue(arg)
|
|
1060
|
+
except Exception as e:
|
|
1061
|
+
raise TypeError(
|
|
1062
|
+
"operation %s argument must be a FormatValue] "
|
|
1063
|
+
"got %s (value=%s)" % (name, type(arg).__name__, str(arg))
|
|
1064
|
+
) from e
|
|
1065
|
+
else:
|
|
1066
|
+
raise TypeError(
|
|
1067
|
+
"operation %s argument must be a FormatValue] "
|
|
1068
|
+
"got %s (value=%s)" % (name, type(arg).__name__, str(arg))
|
|
1069
|
+
)
|
|
1070
|
+
|
|
927
1071
|
elif opcode_has_argument(opcode):
|
|
928
1072
|
_check_arg_int(arg, name)
|