bytecode 0.16.2__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.
Files changed (61) hide show
  1. {bytecode-0.16.2 → bytecode-0.17.0}/.github/workflows/cis.yml +4 -4
  2. {bytecode-0.16.2 → bytecode-0.17.0}/.github/workflows/docs.yml +1 -1
  3. {bytecode-0.16.2 → bytecode-0.17.0}/.github/workflows/frameworks.yml +2 -2
  4. {bytecode-0.16.2 → bytecode-0.17.0}/.github/workflows/release.yml +10 -11
  5. {bytecode-0.16.2/src/bytecode.egg-info → bytecode-0.17.0}/PKG-INFO +3 -3
  6. {bytecode-0.16.2 → bytecode-0.17.0}/doc/changelog.rst +26 -0
  7. {bytecode-0.16.2 → bytecode-0.17.0}/pyproject.toml +15 -3
  8. {bytecode-0.16.2 → bytecode-0.17.0}/src/bytecode/cfg.py +2 -3
  9. {bytecode-0.16.2 → bytecode-0.17.0}/src/bytecode/concrete.py +33 -4
  10. {bytecode-0.16.2 → bytecode-0.17.0}/src/bytecode/flags.py +2 -2
  11. {bytecode-0.16.2 → bytecode-0.17.0}/src/bytecode/instr.py +152 -8
  12. {bytecode-0.16.2 → bytecode-0.17.0}/src/bytecode/utils.py +1 -0
  13. {bytecode-0.16.2 → bytecode-0.17.0}/src/bytecode/version.py +2 -2
  14. {bytecode-0.16.2 → bytecode-0.17.0/src/bytecode.egg-info}/PKG-INFO +3 -3
  15. {bytecode-0.16.2 → bytecode-0.17.0}/tests/__init__.py +8 -3
  16. {bytecode-0.16.2 → bytecode-0.17.0}/tests/exception_handling_cases.py +18 -18
  17. {bytecode-0.16.2 → bytecode-0.17.0}/tests/frameworks/function.py +1 -1
  18. {bytecode-0.16.2 → bytecode-0.17.0}/tests/test_bytecode.py +124 -56
  19. {bytecode-0.16.2 → bytecode-0.17.0}/tests/test_cfg.py +57 -33
  20. {bytecode-0.16.2 → bytecode-0.17.0}/tests/test_concrete.py +112 -55
  21. {bytecode-0.16.2 → bytecode-0.17.0}/tests/test_instr.py +56 -7
  22. {bytecode-0.16.2 → bytecode-0.17.0}/tests/test_misc.py +182 -15
  23. {bytecode-0.16.2 → bytecode-0.17.0}/tox.ini +1 -1
  24. {bytecode-0.16.2 → bytecode-0.17.0}/.coveragerc +0 -0
  25. {bytecode-0.16.2 → bytecode-0.17.0}/.github/FUNDING.yml +0 -0
  26. {bytecode-0.16.2 → bytecode-0.17.0}/.github/dependabot.yml +0 -0
  27. {bytecode-0.16.2 → bytecode-0.17.0}/.gitignore +0 -0
  28. {bytecode-0.16.2 → bytecode-0.17.0}/.pre-commit-config.yaml +0 -0
  29. {bytecode-0.16.2 → bytecode-0.17.0}/.readthedocs.yaml +0 -0
  30. {bytecode-0.16.2 → bytecode-0.17.0}/COPYING +0 -0
  31. {bytecode-0.16.2 → bytecode-0.17.0}/MANIFEST.in +0 -0
  32. {bytecode-0.16.2 → bytecode-0.17.0}/README.rst +0 -0
  33. {bytecode-0.16.2 → bytecode-0.17.0}/TODO.rst +0 -0
  34. {bytecode-0.16.2 → bytecode-0.17.0}/codecov.yml +0 -0
  35. {bytecode-0.16.2 → bytecode-0.17.0}/doc/Makefile +0 -0
  36. {bytecode-0.16.2 → bytecode-0.17.0}/doc/api.rst +0 -0
  37. {bytecode-0.16.2 → bytecode-0.17.0}/doc/byteplay_codetransformer.rst +0 -0
  38. {bytecode-0.16.2 → bytecode-0.17.0}/doc/cfg.rst +0 -0
  39. {bytecode-0.16.2 → bytecode-0.17.0}/doc/conf.py +0 -0
  40. {bytecode-0.16.2 → bytecode-0.17.0}/doc/index.rst +0 -0
  41. {bytecode-0.16.2 → bytecode-0.17.0}/doc/make.bat +0 -0
  42. {bytecode-0.16.2 → bytecode-0.17.0}/doc/requirements.txt +0 -0
  43. {bytecode-0.16.2 → bytecode-0.17.0}/doc/todo.rst +0 -0
  44. {bytecode-0.16.2 → bytecode-0.17.0}/doc/usage.rst +0 -0
  45. {bytecode-0.16.2 → bytecode-0.17.0}/scripts/frameworks/boto3/run.sh +0 -0
  46. {bytecode-0.16.2 → bytecode-0.17.0}/scripts/frameworks/boto3/setup.sh +0 -0
  47. {bytecode-0.16.2 → bytecode-0.17.0}/setup.cfg +0 -0
  48. {bytecode-0.16.2 → bytecode-0.17.0}/src/bytecode/__init__.py +0 -0
  49. {bytecode-0.16.2 → bytecode-0.17.0}/src/bytecode/bytecode.py +0 -0
  50. {bytecode-0.16.2 → bytecode-0.17.0}/src/bytecode/py.typed +0 -0
  51. {bytecode-0.16.2 → bytecode-0.17.0}/src/bytecode.egg-info/SOURCES.txt +0 -0
  52. {bytecode-0.16.2 → bytecode-0.17.0}/src/bytecode.egg-info/dependency_links.txt +0 -0
  53. {bytecode-0.16.2 → bytecode-0.17.0}/src/bytecode.egg-info/requires.txt +0 -0
  54. {bytecode-0.16.2 → bytecode-0.17.0}/src/bytecode.egg-info/top_level.txt +0 -0
  55. {bytecode-0.16.2 → bytecode-0.17.0}/tests/cell_free_vars_cases.py +0 -0
  56. {bytecode-0.16.2 → bytecode-0.17.0}/tests/frameworks/module.py +0 -0
  57. {bytecode-0.16.2 → bytecode-0.17.0}/tests/frameworks/sitecustomize.py +0 -0
  58. {bytecode-0.16.2 → bytecode-0.17.0}/tests/long_lines_example.py +0 -0
  59. {bytecode-0.16.2 → bytecode-0.17.0}/tests/test_code.py +0 -0
  60. {bytecode-0.16.2 → bytecode-0.17.0}/tests/test_flags.py +0 -0
  61. {bytecode-0.16.2 → 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@v4
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@v4
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
@@ -19,7 +19,7 @@ jobs:
19
19
  name: Docs building
20
20
  runs-on: ubuntu-latest
21
21
  steps:
22
- - uses: actions/checkout@v4
22
+ - uses: actions/checkout@v5
23
23
  - name: Get history and tags for SCM versioning to work
24
24
  run: |
25
25
  git fetch --prune --unshallow
@@ -21,10 +21,10 @@ jobs:
21
21
  strategy:
22
22
  fail-fast: false
23
23
  matrix:
24
- python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]
24
+ python-version: ["3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
25
25
 
26
26
  steps:
27
- - uses: actions/checkout@v4
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@v4
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@v4
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@v4.2.1
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@v4.2.1
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@v2.1.0
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
1
  Metadata-Version: 2.4
2
2
  Name: bytecode
3
- Version: 0.16.2
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,14 +36,14 @@ 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.8
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"
@@ -1,6 +1,32 @@
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
+
4
30
  2025-04-14: Version 0.16.2
5
31
  --------------------------
6
32
 
@@ -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.8"
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 = 42
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 :]:
@@ -871,9 +872,7 @@ class ControlFlowGraph(_bytecode.BaseBytecode):
871
872
  # the new one since the blocks are disconnected.
872
873
  if last_instr.is_final() and temp:
873
874
  old_block.append(TryEnd(try_begins[active_try_begin][-1]))
874
- new_tb = TryBegin(
875
- active_try_begin.target, active_try_begin.push_lasti
876
- )
875
+ new_tb = active_try_begin.copy()
877
876
  block.append(new_tb)
878
877
  # Add this new TryBegin to the map to properly update
879
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 = (bool(c_arg & 1), self.names[c_arg >> 1])
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
- # XXX document attributes
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
- index = self.add(self.names, arg[1])
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
- (_opcode.opmap["LOAD_GLOBAL"], _opcode.opmap["LOAD_ATTR"])
26
- if PY312
27
- else (_opcode.opmap["LOAD_GLOBAL"],)
28
- if PY311
29
- else ()
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):
@@ -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)
@@ -5,3 +5,4 @@ PY310: Final[bool] = sys.version_info >= (3, 10)
5
5
  PY311: Final[bool] = sys.version_info >= (3, 11)
6
6
  PY312: Final[bool] = sys.version_info >= (3, 12)
7
7
  PY313: Final[bool] = sys.version_info >= (3, 13)
8
+ PY314: Final[bool] = sys.version_info >= (3, 14)
@@ -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.2".split(".", 3)
8
+ parts = "0.17.0".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.2"
19
+ __version__ = "0.17.0"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: bytecode
3
- Version: 0.16.2
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,14 +36,14 @@ 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.8
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"