Multiple Unary Operations in Python
Question:
I made a typo in one script while using a SQL
like comment instead of a Python comment, which lead to expression
-- sys.exit()
I took me a while to realize that the exit
works and the program stops.
The most probable explanation is that the -
is considered as an unary operator and that multiple such operators are allowed.
The test case of
x = -----------1
which evaluates to -1
supports it.
Unfortunately I was not able to find the relevant syntax.
The UNARY_NEGATIVE describes byte code, not the Python syntax.
Is my interpretation correct?
Answers:
Yes, one relevant part of the specification is 6.6. Unary arithmetic and bitwise operations, which shows:
u_expr ::= power | "-" u_expr | "+" u_expr | "~" u_expr
This means that multiple unary negations can follow each other, but by itself that only means that something like - - - -x
is valid (with spaces). To prove that ----x
is valid, another fact about Python is needed: unlike in many other languages, in Python --
is not a token. So --x
can only be interpreted as -(-x)
, not as -- x
where --
has its own meaning distinct from two negations. There isn’t really anywhere in particular in the spec that positively says that --
is not token, it’s a fact by omission, eg you can check the full grammar for mentions of --
and confirm that it isn’t there.
What is happening is that the unary expression will only raise an error once they have determined that the data type they are decorating is an invalid type at which point it will raise a TypeError
. However in order for it to determine the data type it needs to evaluate the rest of the expression, which in your case is sys.exit()
which raises its exception and exits the program, thus never giving the unary expression a chance to raise the TypeError
This can be demonstrated by creating a function the throws a generic expression and performing and testing it with any of the unary operators, since they all have the same priority level.
This is an example of the TypeError typically associated with invalid unary expresions:
>>> ++++ "1"
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: bad operand type for unary +: 'str'
Then with a function that raises an generic Exception
>>> def example():
... raise Exception("This is not a TypeError")
...
>>> ++++ example()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in example
Exception: This is not a TypeError
In your example you never get to see the error message because it immediately exits the program.
I made a typo in one script while using a SQL
like comment instead of a Python comment, which lead to expression
-- sys.exit()
I took me a while to realize that the exit
works and the program stops.
The most probable explanation is that the -
is considered as an unary operator and that multiple such operators are allowed.
The test case of
x = -----------1
which evaluates to -1
supports it.
Unfortunately I was not able to find the relevant syntax.
The UNARY_NEGATIVE describes byte code, not the Python syntax.
Is my interpretation correct?
Yes, one relevant part of the specification is 6.6. Unary arithmetic and bitwise operations, which shows:
u_expr ::= power | "-" u_expr | "+" u_expr | "~" u_expr
This means that multiple unary negations can follow each other, but by itself that only means that something like - - - -x
is valid (with spaces). To prove that ----x
is valid, another fact about Python is needed: unlike in many other languages, in Python --
is not a token. So --x
can only be interpreted as -(-x)
, not as -- x
where --
has its own meaning distinct from two negations. There isn’t really anywhere in particular in the spec that positively says that --
is not token, it’s a fact by omission, eg you can check the full grammar for mentions of --
and confirm that it isn’t there.
What is happening is that the unary expression will only raise an error once they have determined that the data type they are decorating is an invalid type at which point it will raise a TypeError
. However in order for it to determine the data type it needs to evaluate the rest of the expression, which in your case is sys.exit()
which raises its exception and exits the program, thus never giving the unary expression a chance to raise the TypeError
This can be demonstrated by creating a function the throws a generic expression and performing and testing it with any of the unary operators, since they all have the same priority level.
This is an example of the TypeError typically associated with invalid unary expresions:
>>> ++++ "1"
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: bad operand type for unary +: 'str'
Then with a function that raises an generic Exception
>>> def example():
... raise Exception("This is not a TypeError")
...
>>> ++++ example()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in example
Exception: This is not a TypeError
In your example you never get to see the error message because it immediately exits the program.