I'm trying to understand some 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 testing this out in the shell with some simple conditional statements like this:
>>> 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 can't raise a boolean statement. What does it mean to raise a conditional expression then and why does it work in the __init__
function but not in my test code?
The code is nonsense, a botched attempt at something that looks like an assert
statement that fails, as you have discovered.
What they should have written is:
assert isinstance(matrix, ButtonMatrixElement)
etcetera.
It appears you found decompiled Ableton Live scripts, but the decompilation script produced wrong Python code. The bytecode for an 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 as if whatever automated process was used to decompile the bytecode translated that to the code you see rather than recognise that as an assert
.
Note however that if the isinstance()
call returns True
, the jump instruction (index 12, JUMP_IF_TRUE
) jumps past the RAISE_VARARGS
instruction, while the re-constructed code doesn't. Compare this to an actual raise ... or ...
statement, you'll notice the jump doesn't go past the 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 sophisticated to handle this; if you assume only or
generates JUMP_IF_TRUE
and don't handle the offsets properly, you can see how the error was made.