bytecode 0.18.0__tar.gz → 0.18.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.18.0/src/bytecode.egg-info → bytecode-0.18.1}/PKG-INFO +1 -1
- {bytecode-0.18.0 → bytecode-0.18.1}/doc/changelog.rst +11 -0
- {bytecode-0.18.0 → bytecode-0.18.1}/src/bytecode/cfg.py +12 -2
- {bytecode-0.18.0 → bytecode-0.18.1}/src/bytecode/version.py +2 -2
- {bytecode-0.18.0 → bytecode-0.18.1/src/bytecode.egg-info}/PKG-INFO +1 -1
- {bytecode-0.18.0 → bytecode-0.18.1}/tests/test_cfg.py +73 -0
- {bytecode-0.18.0 → bytecode-0.18.1}/.coveragerc +0 -0
- {bytecode-0.18.0 → bytecode-0.18.1}/.github/FUNDING.yml +0 -0
- {bytecode-0.18.0 → bytecode-0.18.1}/.github/dependabot.yml +0 -0
- {bytecode-0.18.0 → bytecode-0.18.1}/.github/workflows/cis.yml +0 -0
- {bytecode-0.18.0 → bytecode-0.18.1}/.github/workflows/docs.yml +0 -0
- {bytecode-0.18.0 → bytecode-0.18.1}/.github/workflows/frameworks.yml +0 -0
- {bytecode-0.18.0 → bytecode-0.18.1}/.github/workflows/release.yml +0 -0
- {bytecode-0.18.0 → bytecode-0.18.1}/.gitignore +0 -0
- {bytecode-0.18.0 → bytecode-0.18.1}/.pre-commit-config.yaml +0 -0
- {bytecode-0.18.0 → bytecode-0.18.1}/.readthedocs.yaml +0 -0
- {bytecode-0.18.0 → bytecode-0.18.1}/COPYING +0 -0
- {bytecode-0.18.0 → bytecode-0.18.1}/MANIFEST.in +0 -0
- {bytecode-0.18.0 → bytecode-0.18.1}/README.rst +0 -0
- {bytecode-0.18.0 → bytecode-0.18.1}/TODO.rst +0 -0
- {bytecode-0.18.0 → bytecode-0.18.1}/codecov.yml +0 -0
- {bytecode-0.18.0 → bytecode-0.18.1}/doc/Makefile +0 -0
- {bytecode-0.18.0 → bytecode-0.18.1}/doc/api.rst +0 -0
- {bytecode-0.18.0 → bytecode-0.18.1}/doc/byteplay_codetransformer.rst +0 -0
- {bytecode-0.18.0 → bytecode-0.18.1}/doc/cfg.rst +0 -0
- {bytecode-0.18.0 → bytecode-0.18.1}/doc/conf.py +0 -0
- {bytecode-0.18.0 → bytecode-0.18.1}/doc/index.rst +0 -0
- {bytecode-0.18.0 → bytecode-0.18.1}/doc/make.bat +0 -0
- {bytecode-0.18.0 → bytecode-0.18.1}/doc/requirements.txt +0 -0
- {bytecode-0.18.0 → bytecode-0.18.1}/doc/todo.rst +0 -0
- {bytecode-0.18.0 → bytecode-0.18.1}/doc/usage.rst +0 -0
- {bytecode-0.18.0 → bytecode-0.18.1}/pyproject.toml +0 -0
- {bytecode-0.18.0 → bytecode-0.18.1}/scripts/frameworks/boto3/run.sh +0 -0
- {bytecode-0.18.0 → bytecode-0.18.1}/scripts/frameworks/boto3/setup.sh +0 -0
- {bytecode-0.18.0 → bytecode-0.18.1}/setup.cfg +0 -0
- {bytecode-0.18.0 → bytecode-0.18.1}/src/bytecode/__init__.py +0 -0
- {bytecode-0.18.0 → bytecode-0.18.1}/src/bytecode/bytecode.py +0 -0
- {bytecode-0.18.0 → bytecode-0.18.1}/src/bytecode/concrete.py +0 -0
- {bytecode-0.18.0 → bytecode-0.18.1}/src/bytecode/flags.py +0 -0
- {bytecode-0.18.0 → bytecode-0.18.1}/src/bytecode/instr.py +0 -0
- {bytecode-0.18.0 → bytecode-0.18.1}/src/bytecode/py.typed +0 -0
- {bytecode-0.18.0 → bytecode-0.18.1}/src/bytecode/utils.py +0 -0
- {bytecode-0.18.0 → bytecode-0.18.1}/src/bytecode.egg-info/SOURCES.txt +0 -0
- {bytecode-0.18.0 → bytecode-0.18.1}/src/bytecode.egg-info/dependency_links.txt +0 -0
- {bytecode-0.18.0 → bytecode-0.18.1}/src/bytecode.egg-info/requires.txt +0 -0
- {bytecode-0.18.0 → bytecode-0.18.1}/src/bytecode.egg-info/top_level.txt +0 -0
- {bytecode-0.18.0 → bytecode-0.18.1}/tests/__init__.py +0 -0
- {bytecode-0.18.0 → bytecode-0.18.1}/tests/cell_free_vars_cases.py +0 -0
- {bytecode-0.18.0 → bytecode-0.18.1}/tests/exception_handling_cases.py +0 -0
- {bytecode-0.18.0 → bytecode-0.18.1}/tests/frameworks/function.py +0 -0
- {bytecode-0.18.0 → bytecode-0.18.1}/tests/frameworks/module.py +0 -0
- {bytecode-0.18.0 → bytecode-0.18.1}/tests/frameworks/sitecustomize.py +0 -0
- {bytecode-0.18.0 → bytecode-0.18.1}/tests/long_lines_example.py +0 -0
- {bytecode-0.18.0 → bytecode-0.18.1}/tests/test_bytecode.py +0 -0
- {bytecode-0.18.0 → bytecode-0.18.1}/tests/test_code.py +0 -0
- {bytecode-0.18.0 → bytecode-0.18.1}/tests/test_concrete.py +0 -0
- {bytecode-0.18.0 → bytecode-0.18.1}/tests/test_flags.py +0 -0
- {bytecode-0.18.0 → bytecode-0.18.1}/tests/test_instr.py +0 -0
- {bytecode-0.18.0 → bytecode-0.18.1}/tests/test_misc.py +0 -0
- {bytecode-0.18.0 → bytecode-0.18.1}/tests/util_annotation.py +0 -0
- {bytecode-0.18.0 → bytecode-0.18.1}/tox.ini +0 -0
|
@@ -1,6 +1,17 @@
|
|
|
1
1
|
ChangeLog
|
|
2
2
|
=========
|
|
3
3
|
|
|
4
|
+
03-06-2026: Version 0.18.1
|
|
5
|
+
--------------------------
|
|
6
|
+
|
|
7
|
+
Bugfixes:
|
|
8
|
+
|
|
9
|
+
- Fix an ``AssertionError`` in stack-size computation when a single exception
|
|
10
|
+
region is split into multiple ``TryBegin`` instances sharing one handler (as
|
|
11
|
+
produced by bytecode-rewriting tools that wrap a whole function body in a
|
|
12
|
+
single handler). ``TryBegin``/``TryEnd`` are now matched on the handler block
|
|
13
|
+
rather than on ``TryBegin`` identity.
|
|
14
|
+
|
|
4
15
|
03-06-2026: Version 0.18.0
|
|
5
16
|
--------------------------
|
|
6
17
|
|
|
@@ -343,7 +343,17 @@ class _StackSizeComputer:
|
|
|
343
343
|
# current try begin. However inside the CFG some blocks may
|
|
344
344
|
# start with a TryEnd relevant only when reaching this block
|
|
345
345
|
# through a particular jump. So we are lenient here.
|
|
346
|
-
|
|
346
|
+
#
|
|
347
|
+
# We match on the exception handler (the TryBegin target block)
|
|
348
|
+
# rather than on the TryBegin instance: a single exception region
|
|
349
|
+
# can be split into several TryBegin copies that share the same
|
|
350
|
+
# handler (see ``from_bytecode``), and the copy carried over as
|
|
351
|
+
# ``pending_try_begin`` through a jump is not necessarily the same
|
|
352
|
+
# instance as the one referenced by this block's leading TryEnd.
|
|
353
|
+
if (
|
|
354
|
+
self._current_try_begin is None
|
|
355
|
+
or instr.entry.target is not self._current_try_begin.target
|
|
356
|
+
):
|
|
347
357
|
continue
|
|
348
358
|
|
|
349
359
|
# Compute the stack usage of the exception handler
|
|
@@ -403,7 +413,7 @@ class _StackSizeComputer:
|
|
|
403
413
|
if (
|
|
404
414
|
(te := self.block.get_trailing_try_end(i))
|
|
405
415
|
and self._current_try_begin is not None
|
|
406
|
-
and te.entry is self._current_try_begin
|
|
416
|
+
and te.entry.target is self._current_try_begin.target
|
|
407
417
|
):
|
|
408
418
|
assert isinstance(te.entry.target, BasicBlock)
|
|
409
419
|
yield from self._compute_exception_handler_stack_usage(
|
|
@@ -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.18.
|
|
8
|
+
parts = "0.18.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.18.
|
|
19
|
+
__version__ = "0.18.1"
|
|
@@ -18,6 +18,7 @@ from bytecode import (
|
|
|
18
18
|
Label,
|
|
19
19
|
SetLineno,
|
|
20
20
|
TryBegin,
|
|
21
|
+
TryEnd,
|
|
21
22
|
dump_bytecode,
|
|
22
23
|
)
|
|
23
24
|
from bytecode.utils import PY312, PY313, PY314
|
|
@@ -903,6 +904,78 @@ class CFGStacksizeComputationTests(TestCase):
|
|
|
903
904
|
self.assertEqual(test([], name=None), -1)
|
|
904
905
|
self.assertEqual(stdout.getvalue(), "second finally\nfirst finally\n")
|
|
905
906
|
|
|
907
|
+
def test_stack_size_computation_shared_handler_split_regions(self):
|
|
908
|
+
# Regression test for an assertion failure in the stack-size
|
|
909
|
+
# computation. A bytecode-rewriting transform (e.g. an instrumentation
|
|
910
|
+
# tool) may wrap a whole function body in a single exception handler
|
|
911
|
+
# whose coverage is split around the body's own try blocks, so that the
|
|
912
|
+
# regions never nest but all share the *same* handler block. A single
|
|
913
|
+
# logical region then maps to several TryBegin instances.
|
|
914
|
+
#
|
|
915
|
+
# When such a region is exited through a forward jump (here the branch
|
|
916
|
+
# inside the first handler body), the jump target block is reached both
|
|
917
|
+
# by fall-through and by the jump, and ends up carrying one TryBegin
|
|
918
|
+
# copy as ``pending_try_begin`` while its own leading TryEnd references
|
|
919
|
+
# a *different* copy of the same region. Matching TryEnd against the
|
|
920
|
+
# current TryBegin by identity then failed to close the region, leaving
|
|
921
|
+
# it spuriously open and tripping ``assert self._current_try_begin is
|
|
922
|
+
# None`` at the following TryBegin. Matching must be done on the handler
|
|
923
|
+
# (the TryBegin target block) instead.
|
|
924
|
+
if sys.version_info < (3, 11):
|
|
925
|
+
self.skipTest("exception tables (TryBegin/TryEnd) require 3.11+")
|
|
926
|
+
|
|
927
|
+
def trigger(d, x): # pragma: no cover
|
|
928
|
+
try:
|
|
929
|
+
d["a"]
|
|
930
|
+
except KeyError:
|
|
931
|
+
if x: # a branch *inside* the handler body
|
|
932
|
+
pass
|
|
933
|
+
else:
|
|
934
|
+
d["b"] = 1
|
|
935
|
+
try: # ... followed by another try block
|
|
936
|
+
del d["c"]
|
|
937
|
+
except KeyError:
|
|
938
|
+
pass
|
|
939
|
+
return 99
|
|
940
|
+
|
|
941
|
+
bc = Bytecode.from_code(trigger.__code__)
|
|
942
|
+
|
|
943
|
+
# Wrap the whole body in one shared handler, splitting the covering
|
|
944
|
+
# region around the body's existing try blocks.
|
|
945
|
+
except_label = Label()
|
|
946
|
+
first_tb = last_tb = TryBegin(except_label, push_lasti=True)
|
|
947
|
+
i = 0
|
|
948
|
+
while i < len(bc):
|
|
949
|
+
instr = bc[i]
|
|
950
|
+
if isinstance(instr, TryBegin) and last_tb is not None:
|
|
951
|
+
bc.insert(i, TryEnd(last_tb))
|
|
952
|
+
last_tb = None
|
|
953
|
+
i += 1
|
|
954
|
+
elif isinstance(instr, TryEnd):
|
|
955
|
+
j = i + 1
|
|
956
|
+
while j < len(bc) and not isinstance(bc[j], TryBegin):
|
|
957
|
+
if isinstance(bc[j], Instr):
|
|
958
|
+
last_tb = TryBegin(except_label, push_lasti=True)
|
|
959
|
+
bc.insert(i + 1, last_tb)
|
|
960
|
+
break
|
|
961
|
+
j += 1
|
|
962
|
+
i += 1
|
|
963
|
+
i += 1
|
|
964
|
+
bc.insert(0, first_tb)
|
|
965
|
+
bc.append(TryEnd(last_tb))
|
|
966
|
+
bc.append(except_label)
|
|
967
|
+
bc.append(Instr("PUSH_EXC_INFO"))
|
|
968
|
+
bc.append(Instr("RERAISE", 2))
|
|
969
|
+
|
|
970
|
+
cfg = ControlFlowGraph.from_bytecode(bc)
|
|
971
|
+
# Used to raise AssertionError on 3.12.
|
|
972
|
+
cfg.compute_stacksize()
|
|
973
|
+
|
|
974
|
+
# The rewritten code object is valid and still runs correctly.
|
|
975
|
+
trigger.__code__ = cfg.to_code()
|
|
976
|
+
self.assertEqual(trigger({}, True), 99)
|
|
977
|
+
self.assertEqual(trigger({"c": 1}, False), 99)
|
|
978
|
+
|
|
906
979
|
def test_stack_size_with_dead_code(self):
|
|
907
980
|
# Simply demonstrate more directly the previously mentioned issue.
|
|
908
981
|
def test(*args): # pragma: no cover
|
|
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
|
|
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
|