Reputation: 3364
Let me start off by saying: this is to be used for esoteric purposes - not production code. I'm playing around with doing stuff in a single line of Python code, hence my need for expressions and not statements. (EDIT: I'm working on mechanically compiling code to single line of (mostly) equivalent Python code, BitBucket - onelinepython. Note it's very work in progress, hence my reticence in initially mentioning it)
I essentially want to do two things:
Call a function that raises an exception instance of my choosing something like:
raise_exception(WhateverException())
Run a function in an enclosed environment where I can get the exception instance that is raised, if one is raised, and, otherwise, the return value of the function that was called. E.g.:
has_exception, return_or_exception = check_exception(f, param1, param2, ...)
Ideally, I want to do this with some default library or built-in function (no matter how much I have to bastardise its intended use). I don't need functions that have the exact same signatures as the examples I provided, just something I can mangle into something close enough. I do have one restriction, though: no use of eval() or equivalent functions.
EDIT: I know I could define my own functions to do this, but then they would still have to follow the restriction that they are a single expression. So solutions that use raise
and try
inside a function definition are out. Function definitions, raise-statement and try-blocks are unfortunately statements and not expressions.
As for any solutions I've tried. The answer is none yet. The closest I have to an idea of how to solve this is by misusing unittest
's assert functionality, but I think that is a dead-end.
EDIT 2: To make it clear, I'm fine with using a module or such that uses raise
-statements or try
-blocks somewhere in its code. My goal is to take some code and turn it into an equivalent single line of code (which includes any helper functions I may be using). But since I want this to work on a default installation of Python I want to only use default libraries.
Upvotes: 4
Views: 1785
Reputation: 157484
To raise an exception:
>>> import warnings
>>> WV = type("WV", (Warning, ValueError), {})
>>> warnings.simplefilter("error", WV)
>>> warnings.warn("wv", WV)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
__main__.WV: wv
To catch an exception:
>>> import unittest
>>> res = type("TR", (unittest.TestResult, ), dict(addError=lambda self, test, err: setattr(self, '_last_err', err)))()
>>> unittest.FunctionTestCase(lambda: [][0])(res)
>>> res._last_err
(<type 'exceptions.IndexError'>, IndexError('list index out of range',), <traceback object at 0x2b4358e69950>)
Note that the warnings
method will only work for exceptions that derive from Warning
, but you should always be able to multiply-inherit; here's an example:
>>> WS = type("WS", (Warning, StopIteration), {})
>>> warnings.simplefilter("error", WS)
>>> list(type("R", (object,), dict(__init__=lambda self, stop: (setattr(self, 'stop', stop), setattr(self, 'i', 0), None)[-1], __iter__=lambda self: self, next=lambda self: (self.i, setattr(self, 'i', self.i + 1))[0] if self.i < self.stop else warnings.warn("Stop", WS)))(5))
[0, 1, 2, 3, 4]
Upvotes: 2
Reputation: 172367
You are asking how to raise an exception without using raise
and catch an exception without using except
. Your reluctance to use these statements is because you can't use more than one statement in a single line of code, and you have the idea to compile Python modules into oneliners.
Short answer: Well, you can't.
And even if you could, why would you? It's a completely meaningless effort. The code is not faster or even significantly smaller because it is in one line. It goes against the idea of Python as well. And if you want to obfuscate it, there are much better ways, including compiling it to bytecode.
Longer answer:
You could implement your own exception system, independent of the Python exceptions, but that would be astonishingly slow, and this would still not catch the Python exceptions, so it's not useful in your case.
For the raise
-statement, you could re-implementing the raise statement as a function in C, but this you seem to think is cheating, and I also don't see how it would be possible with other statements, such as except
.
You could also move out some statements into functions in a separate module, but this is of course then no longer actually a one-liner module in any meaningful way, and not all statements are easily wrapped like this, except
being the most relevant case here. You'd have to wrap the whole try/except
block, but the resulting function would in turn also only take expressions as parameters, so you would have to extract the blocks into functions, and you'd end up needing to basically re-implement most of Python as a statement-less language, which is silly. And you'd end up with the helper functions in a separate module, which you don't want to.
So the answer to your question of how to raise an exception without using raise
and catch an exception without using except
is "You don't".
Upvotes: -1
Reputation: 251568
This answer suggests that catching exceptions with an expression is not possible in general. I'm also pretty sure that it's not possible to raise an arbitrary exception without using raise
. (You can generate some particular exceptions with expressions like 1/0
or dict['keyThatWillNeverExist']
, but not any arbitrary exception with arbitrary exception info.)
The language reference says:
The Python interpreter raises an exception when it detects a run-time error (such as division by zero). A Python program can also explicitly raise an exception with the raise statement. Exception handlers are specified with the try ... except statement.
Although this doesn't rule out the possibility that some dark corner of the language specification allows raising exceptions in other ways, the statement is pretty straightforward: you raise exceptions with raise
and catch them with try/except
.
Note that using unittest, or any other Python library, is unlikely to be a real solution in your sense, because unittest contains functions written in Python that use try/except. So if you're okay with using unittest, you ought to be okay with writing your own functions.
I imagine it might be possible to achieve your goal by "cheating" and writing a C extension that provides functions doing what you want. But that's not really converting it to equivalent Python code.
Upvotes: 1
Reputation: 375932
You can define your own functions to do this:
def raise_exception(ex):
raise ex
def check_exception(f, *args, **kwargs):
try:
return False, f(*args, **kwargs)
except Exception as e:
return True, e
Upvotes: 1