"raise" followed by a conditional expression (python)
I am trying to understand the python 2.5 code and I came across this pattern:
def __init__(self, matrix, top_buttons, side_buttons, config_button):
raise isinstance(matrix, ButtonMatrixElement) or AssertionError
raise matrix.width() == 8 and matrix.height() == 8 or AssertionError
raise isinstance(top_buttons, tuple) or AssertionError
raise len(top_buttons) == 8 or AssertionError
raise isinstance(side_buttons, tuple) or AssertionError
raise len(side_buttons) == 8 or AssertionError
raise isinstance(config_button, ButtonElement) or AssertionError
I tried to test this in the shell with some simple conditional statements like:
>>> str = 'hello'
>>> raise len(str) == 5 or AssertionError
Traceback (most recent call last):
File "<pyshell#6>", line 1, in <module>
raise len(str) == 5 or AssertionError
TypeError: exceptions must be classes, instances, or strings (deprecated), not bool
So, judging from this test, at least the way I tried it, you cannot raise a boolean assertion. What does it mean to raise a conditional expression and why does it work in a function __init__
but not in my test code?
source to share
The code is silly, a failed attempt at something that looks like a assert
statement that doesn't work as you discovered.
What they should have written:
assert isinstance(matrix, ButtonMatrixElement)
etc.
It looks like you discovered decompiled Ableton Live scripts , but the decompiled script caused the wrong Python code. The bytecode for assert
looks like this (Python 2.5 bytecode):
>>> import dis
>>> dis.dis(compile('''assert isinstance(matrix, ButtonMatrixElement)''', '<stdin>', 'exec'))
1 0 LOAD_NAME 0 (isinstance)
3 LOAD_NAME 1 (matrix)
6 LOAD_NAME 2 (ButtonMatrixElement)
9 CALL_FUNCTION 2
12 JUMP_IF_TRUE 7 (to 22)
15 POP_TOP
16 LOAD_GLOBAL 3 (AssertionError)
19 RAISE_VARARGS 1
>> 22 POP_TOP
23 LOAD_CONST 0 (None)
26 RETURN_VALUE
and it looks like some kind of automatic process was used to decompile the bytecode translated into the code you see, rather than recognize it as assert
.
Note that if the call isinstance()
returns True
, the branch instruction (index 12, JUMP_IF_TRUE
) jumps past the instruction RAISE_VARARGS
, but the rebuilt code does not. Compare this to the actual statement raise ... or ...
, you will notice that the jump does not pass by raise
:
>>> dis.dis(compile('raise foo or bar', '<stdin>', 'exec'))
1 0 LOAD_NAME 0 (foo)
3 JUMP_IF_TRUE 4 (to 10)
6 POP_TOP
7 LOAD_NAME 1 (bar)
>> 10 RAISE_VARARGS 1
13 LOAD_CONST 0 (None)
16 RETURN_VALUE
Presumably the code generator was just not hard to handle; if you assume you are only or
generating JUMP_IF_TRUE
and not handling offsets as expected, you can see how the error was made.
source to share
As stated earlier, this is a bug in the decompiler that was used in the python-based bytecode reverse engineering process. If you want to decompile the files yourself, you can use the following version of the script:
https://github.com/frankebersoll/uncompyle2
The above error should be fixed. When you decompile MainSelectorComponent.pyc
with this version, you get:
class MainSelectorComponent(ModeSelectorComponent):
""" Class that reassigns the button on the launchpad to different functions """
def __init__(self, matrix, top_buttons, side_buttons, config_button):
assert isinstance(matrix, ButtonMatrixElement)
assert matrix.width() == 8 and matrix.height() == 8
assert isinstance(top_buttons, tuple)
assert len(top_buttons) == 8
assert isinstance(side_buttons, tuple)
assert len(side_buttons) == 8
assert isinstance(config_button, ButtonElement)
source to share