How active exception to reraise in Python(3.8<=)? - python

look at this:
RuntimeError: No active exception to reraise
I use raise. with out error like this:
class example:
def __getattribute__(self, attr_name):
raise # I mean: AttributeError: '...' object has no attribute '...'
This is raise statement:
raise_stmt ::= "raise" [expression ["from" expression]]
expression is OPTIONAL.
I check this, but this isn't my answer. if error says "No active exception to reraise", so I can active an error. I do not know what this error means. My question is, what is meant by "active exception" and where it is used? Does it help make the code shorter and more optimized? Is it possible to use it for the task I showed a little higher in the code?

When you use raise keyword barely, Python tries to re-raise the currently occurred exception in the current scope, If there is no exception triggered on, you will get RuntimeError: No active exception to re-raise.
To see which exception is active(being handled), you can use sys.exc_info():
import sys
try:
raise ZeroDivisionError()
except ZeroDivisionError:
type_, value, tb = sys.exc_info()
print(type_) # <class 'ZeroDivisionError'>
In the above except block you can use bare raise keyword, which re-raised the ZeroDivisionError exception for you.
If there is no active exception, the returned value of sys.exc_info() is (None, None, None). So you have to use raise keyword followed by a subclass or an instance of BaseException. This is the case in your question inside __getattribute__ method since there is no active exception.
class Example:
def __getattribute__(self, attr_name):
raise AttributeError(f'Error for "{attr_name}".')
obj = Example()
obj.foo # AttributeError: Error for "foo".
From comments:
Active exception means the exception that is currently triggered on, and is in the workflow, If you don't catch it and let it bubbles up, it will terminate the process.

I can find this for my questions:
what is meant by "active exception" and where it is used?
then using try-except and code goes to except block, error actives.
about usage we can check error and if Should not be handled, can use
raise without arg (Even In Functions). now error raised without
Reference to line. I do not know if there is another way to do this or not, but I was convinced of this method.
Example:
>>> try:
... 10 / 0
... except[ ZeroDivisionError[ as err]]:
... raise
...
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
ZeroDivisionError: division by zero
>>> def test():
... raise
...
>>> try:
... 10 / 0
... except[ ZeroDivisionError[ as err]]:
... test()
...
Traceback (most recent call last):
File "<stdin>", line 4, in <module>
File "<stdin>", line 2, in <module>
ZeroDivisionError: division by zero
>>>
Does it help make the code shorter and more optimized?
Yes. By using this method, the code and error are shortened. But in this method, there is less control over the exception procedure, which can sometimes be problematic.
Is it possible to use it for the task I showed a little higher in the code?
No. NotImplemented must be returned for the above code to take effect. I have full confidence in this, but there is a possibility that this alone is not enough.

Related

Is there any possible way to check the exception when its defined as e? [duplicate]

some_function() raises an exception while executing, so the program jumps to the except:
try:
some_function()
except:
print("exception happened!")
How do I see what caused the exception to occur?
The other answers all point out that you should not catch generic exceptions, but no one seems to want to tell you why, which is essential to understanding when you can break the "rule". Here is an explanation. Basically, it's so that you don't hide:
the fact that an error occurred
the specifics of the error that occurred (error hiding antipattern)
So as long as you take care to do none of those things, it's OK to catch the generic exception. For instance, you could provide information about the exception to the user another way, like:
Present exceptions as dialogs in a GUI
Transfer exceptions from a worker thread or process to the controlling thread or process in a multithreading or multiprocessing application
So how to catch the generic exception? There are several ways. If you just want the exception object, do it like this:
try:
someFunction()
except Exception as ex:
template = "An exception of type {0} occurred. Arguments:\n{1!r}"
message = template.format(type(ex).__name__, ex.args)
print message
Make sure message is brought to the attention of the user in a hard-to-miss way! Printing it, as shown above, may not be enough if the message is buried in lots of other messages. Failing to get the users attention is tantamount to swallowing all exceptions, and if there's one impression you should have come away with after reading the answers on this page, it's that this is not a good thing. Ending the except block with a raise statement will remedy the problem by transparently reraising the exception that was caught.
The difference between the above and using just except: without any argument is twofold:
A bare except: doesn't give you the exception object to inspect
The exceptions SystemExit, KeyboardInterrupt and GeneratorExit aren't caught by the above code, which is generally what you want. See the exception hierarchy.
If you also want the same stacktrace you get if you do not catch the exception, you can get that like this (still inside the except clause):
import traceback
print traceback.format_exc()
If you use the logging module, you can print the exception to the log (along with a message) like this:
import logging
log = logging.getLogger()
log.exception("Message for you, sir!")
If you want to dig deeper and examine the stack, look at variables etc., use the post_mortem function of the pdb module inside the except block:
import pdb
pdb.post_mortem()
I've found this last method to be invaluable when hunting down bugs.
Get the name of the class that exception object belongs:
e.__class__.__name__
and using print_exc() function will also print stack trace which is essential info for any error message.
Like this:
from traceback import print_exc
class CustomException(Exception): pass
try:
raise CustomException("hi")
except Exception as e:
print ('type is:', e.__class__.__name__)
print_exc()
# print("exception happened!")
You will get output like this:
type is: CustomException
Traceback (most recent call last):
File "exc.py", line 7, in <module>
raise CustomException("hi")
CustomException: hi
And after print and analysis, the code can decide not to handle exception and just execute raise:
from traceback import print_exc
class CustomException(Exception): pass
def calculate():
raise CustomException("hi")
try:
calculate()
except CustomException as e:
# here do some extra steps in case of CustomException
print('custom logic doing cleanup and more')
# then re raise same exception
raise
Output:
custom logic doing cleanup and more
And interpreter prints exception:
Traceback (most recent call last):
File "test.py", line 9, in <module>
calculate()
File "test.py", line 6, in calculate
raise CustomException("hi")
__main__.CustomException: hi
After raise original exception continues to propagate further up the call stack. (Beware of possible pitfall) If you raise new exception it caries new (shorter) stack trace.
from traceback import print_exc
class CustomException(Exception):
def __init__(self, ok):
self.ok = ok
def calculate():
raise CustomException(False)
try:
calculate()
except CustomException as e:
if not e.ok:
# Always use `raise` to rethrow exception
# following is usually mistake, but here we want to stress this point
raise CustomException(e.ok)
print("handling exception")
Output:
Traceback (most recent call last):
File "test.py", line 13, in <module>
raise CustomException(e.message)
__main__.CustomException: hi
Notice how traceback does not include calculate() function from line 9 which is the origin of original exception e.
You usually should not catch all possible exceptions with try: ... except as this is overly broad. Just catch those that are expected to happen for whatever reason. If you really must, for example if you want to find out more about some problem while debugging, you should do
try:
...
except Exception as ex:
print ex # do whatever you want for debugging.
raise # re-raise exception.
Most answers point to except (…) as (…): syntax (rightly so) but at the same time nobody wants to talk about an elephant in the room, where the elephant is sys.exc_info() function.
From the documentation of sys module (emphasis mine):
This function returns a tuple of three values that give information
about the exception that is currently being handled.
(…)
If no exception is being handled anywhere on the stack, a tuple
containing three None values is returned. Otherwise, the values
returned are (type, value, traceback). Their meaning is: type gets the
type of the exception being handled (a subclass of BaseException);
value gets the exception instance (an instance of the exception type);
traceback gets a traceback object (see the Reference Manual) which
encapsulates the call stack at the point where the exception
originally occurred.
I think the sys.exc_info() could be treated as the most direct answer to the original question of How do I know what type of exception occurred?
These answers are fine for debugging, but for programmatically testing the exception, isinstance(e, SomeException) can be handy, as it tests for subclasses of SomeException too, so you can create functionality that applies to hierarchies of exceptions.
Unless somefunction is a very bad coded legacy function, you shouldn't need what you're asking.
Use multiple except clause to handle in different ways different exceptions:
try:
someFunction()
except ValueError:
# do something
except ZeroDivision:
# do something else
The main point is that you shouldn't catch generic exception, but only the ones that you need to. I'm sure that you don't want to shadow unexpected errors or bugs.
In Python 2, the following are useful
except Exception, exc:
# This is how you get the type
excType = exc.__class__.__name__
# Here we are printing out information about the Exception
print 'exception type', excType
print 'exception msg', str(exc)
# It's easy to reraise an exception with more information added to it
msg = 'there was a problem with someFunction'
raise Exception(msg + 'because of %s: %s' % (excType, exc))
Use type class and as statement
try:#code
except Exception as e:
m=type(e)
#m is the class of the exception
strm=str(m)
#strm is the string of m
Hope this will help a little more
import sys
varExcepHandling, varExcepHandlingZer = 2, 0
try:
print(varExcepHandling/varExcepHandlingZer)
except Exception as ex:
print(sys.exc_info())
'sys.exc_info()' will return a tuple, if you only want the exception class name use 'sys.exc_info()[0]'
Note:- if you want to see all the exception classes just write dir(__builtin__)
Here's how I'm handling my exceptions. The idea is to do try solving the issue if that's easy, and later add a more desirable solution if possible. Don't solve the issue in the code that generates the exception, or that code loses track of the original algorithm, which should be written to-the-point. However, pass what data is needed to solve the issue, and return a lambda just in case you can't solve the problem outside of the code that generates it.
path = 'app.p'
def load():
if os.path.exists(path):
try:
with open(path, 'rb') as file:
data = file.read()
inst = pickle.load(data)
except Exception as e:
inst = solve(e, 'load app data', easy=lambda: App(), path=path)()
else:
inst = App()
inst.loadWidgets()
# e.g. A solver could search for app data if desc='load app data'
def solve(e, during, easy, **kwargs):
class_name = e.__class__.__name__
print(class_name + ': ' + str(e))
print('\t during: ' + during)
return easy
For now, since I don't want to think tangentially to my app's purpose, I haven't added any complicated solutions. But in the future, when I know more about possible solutions (since the app is designed more), I could add in a dictionary of solutions indexed by during.
In the example shown, one solution might be to look for app data stored somewhere else, say if the 'app.p' file got deleted by mistake.
For now, since writing the exception handler is not a smart idea (we don't know the best ways to solve it yet, because the app design will evolve), we simply return the easy fix which is to act like we're running the app for the first time (in this case).
To add to Lauritz's answer, I created a decorator/wrapper for exception handling and the wrapper logs which type of exception occurred.
class general_function_handler(object):
def __init__(self, func):
self.func = func
def __get__(self, obj, type=None):
return self.__class__(self.func.__get__(obj, type))
def __call__(self, *args, **kwargs):
try:
retval = self.func(*args, **kwargs)
except Exception, e :
logging.warning('Exception in %s' % self.func)
template = "An exception of type {0} occured. Arguments:\n{1!r}"
message = template.format(type(e).__name__, e.args)
logging.exception(message)
sys.exit(1) # exit on all exceptions for now
return retval
This can be called on a class method or a standalone function with the decorator:
#general_function_handler
See my blog about for the full example: http://ryaneirwin.wordpress.com/2014/05/31/python-decorators-and-exception-handling/
You can start as Lauritz recommended, with:
except Exception as ex:
and then just to print ex like so:
try:
#your try code here
except Exception as ex:
print ex
Your question is: "How can I see exactly what happened in the someFunction() that caused the exception to happen?"
It seems to me that you are not asking about how to handle unforeseen exceptions in production code (as many answers assumed), but how to find out what is causing a particular exception during development.
The easiest way is to use a debugger that can stop where the uncaught exception occurs, preferably not exiting, so that you can inspect the variables. For example, PyDev in the Eclipse open source IDE can do that. To enable that in Eclipse, open the Debug perspective, select Manage Python Exception Breakpoints in the Run menu, and check Suspend on uncaught exceptions.
Use the below for both Exception type and Exception text
import sys
print(str(sys.exc_info()[0]).split(' ')[1].strip('>').strip("'")+"-"+(str(sys.exc_info()[1])))
if you want only exception type: Use -->
import sys
print(str(sys.exc_info()[0]).split(' ')[1].strip('>').strip("'"))
Thanks Rajeshwar
The actual exception can be captured in the following way:
try:
i = 1/0
except Exception as e:
print e
You can learn more about exceptions from The Python Tutorial.
Just refrain from catching the exception and the traceback that Python prints will tell you what exception occurred.

Why python introspection doesn't know exceptions in a function?

When I handle some code exception code, I wander why python didn't include what exception will the function raise in its introspect system.For example,when I have to use a function refer to many other function that will raise different exceptions,I have to consider all that happen in my business logic.
Like this:
def a():
raise Exception('exception1')
def b():
a()
raise Exception('exception2')
def c():
b()
raise Exception('exception3')
def business():
try:
c()
except Exception as e:
pass
I have to keep digging in the function calls between them that I can know what maybe raise in this code block.And introspect system does not have information of exception.
And as I know, Java will explicitly annotated 'Throw' in function definition,and IDE and programmer can easily know what kinds of exception should I handle.
It will be better if I can know all the exception with object itself,for example:
all_exception = obj.__exceptions__()
So,my question is,why python not include exception introspect in function object.
Who can explain python's design?
Python is a dynamic language, and you can't know, up front, what exceptions a function could throw.
Take this example:
def throw(exception):
raise exception
What exception will that function raise? I can use throw(ValueError) or throw(TypeError('foobar')), and both would work and are valid Python:
>>> throw(ValueError)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in throw
ValueError
>>> throw(TypeError('foobar'))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in throw
TypeError: foobar
Exceptions are just classes and instances. Current versions of Python require that the exception class must derive from BaseException, but in old Python versions you could even use strings for exceptions (raise "Your mother was a hamster").
And because they are looked up as globals and are not reserved names, you can assign different exceptions to names. The following is legal Python syntax too:
>>> def oops():
... raise ValueError('Oops')
...
>>> ValueError = TypeError
>>> oops()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in oops
TypeError: Oops
That's why Python functions can't expose what exceptions they raise.
Note that there is never a good reason to use plain Exception. Use one of the standard exceptions where they make sense (ValueError, TypeError, IndexError, KeyError, etc.) or create your own API-specific exceptions by subclassing from Exception or a more specific exception subclass.
Then document your API properly. State what exceptions a developer should expect, where needed. The standard exceptions don't need to be spelled out; it is reasonably obvious that a function that only works on strings will throw TypeError if you pass in a file object instead.
You can use a exception class hierarchy in your business application if you need to catch multiple types:
class BusinessException(Exception):
"""The base exception for all of Business APIs"""
class SpecificBusinessException(BusinessException):
"""Exception that indicates a specific problem occurred"""
class DifferenBusinessException(BusinessException):
"""Exception that indicates a different specific problem occurred"""
then raise the subclassed exceptions and catch BusinessException to handle all, or catch only specific subclasses to customise handling.
If you must figure out what exceptions code raise and accept the risks involved with a dynamic language being able to change the names, then you could use abstract syntax tree (AST) analysis to at least find some information on exceptions. For straight raise Name and raise Name(args..) statements, extracting those names or calls by walking the AST is at least relatively straightforward:
import builtins
import inspect
import ast
class ExceptionExtractor(ast.NodeVisitor):
def __init__(self):
self.exceptions = []
def visit_Raise(self, node):
if node.exc is None:
# plain re-raise
return
exc_name = node.exc
if isinstance(exc_name, ast.Call):
exc_name = exc_name.func
if not (isinstance(exc_name, ast.Name) and
isinstance(exc_name.ctx, ast.Load)):
# not a raise Name or raise Name(...)
return
self.exceptions.append(exc_name.id)
def global_exceptions_raised(func):
"""Extract the expressions used in raise statements
Only supports raise Name and raise Name(...) forms, and
only if the source can be accessed. No checks are made for the
scope of the name.
returns references to those exception names that can be loaded from
the function globals.
"""
source = inspect.getsource(func)
tree = ast.parse(source)
extractor = ExceptionExtractor()
extractor.visit(tree)
fglobals = {**func.__globals__, **vars(builtins)}
return [fglobals[name] for name in extractor.exceptions if name in fglobals]

How do I use GeneratorExit?

I have the following mcve:
import logging
class MyGenIt(object):
def __init__(self, name, content):
self.name = name
self.content = content
def __iter__(self):
with self:
for o in self.content:
yield o
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
if exc_type:
logging.error("Aborted %s", self,
exc_info=(exc_type, exc_value, traceback))
And here is sample use:
for x in MyGenIt("foo",range(10)):
if x == 5:
raise ValueError("got 5")
I would like logging.error to report the ValueError, but instead it reports GeneratorExit:
ERROR:root:Aborted <__main__.MyGenIt object at 0x10ca8e350>
Traceback (most recent call last):
File "<stdin>", line 8, in __iter__
GeneratorExit
When I catch GeneratorExit in __iter__:
def __iter__(self):
with self:
try:
for o in self.content:
yield o
except GeneratorExit:
return
nothing is logged (of course) because __exit__ is called with exc_type=None.
Why do I see GeneratorExit instead of ValueError in __exit__?
What do I do to get the desired behavior, i.e., ValueError in __exit__?
Just a quick note that you could "bring the context manager out" of the generator, and by only changing 3 lines get:
import logging
class MyGenIt(object):
def __init__(self, name, content):
self.name = name
self.content = content
def __iter__(self):
for o in self.content:
yield o
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
if exc_type:
logging.error("Aborted %s", self,
exc_info=(exc_type, exc_value, traceback))
with MyGenIt("foo", range(10)) as gen:
for x in gen:
if x == 5:
raise ValueError("got 5")
A context manager that could also act as an iterator -- and would catch caller code exceptions like your ValueError.
The basic problem is that you are trying to use a with statement inside the generator to catch an exception that is raised outside the generator. You cannot get __iter__ to see the ValueError, because __iter__ is not executing at the time the ValueError is raised.
The GeneratorExit exception is raised when the generator itself is deleted, which happens when it is garbage collected. As soon as the exception occurs, the for loop terminates; since the only reference to the generator (the object obtained by calling __iter__) is in the loop expression, terminating the loop removes the only reference to the iterator and makes it available for garbage collection. It appears that here it is being garbage collected immediately, meaning that the GeneratorExit exception happens between the raising of the ValueError and the propagation of that ValueError to the enclosing code. The GeneratorExit is normally handled totally internally; you are only seeing it because your with statement is inside the generator itself.
In other words, the flow goes something like this:
Exception is raised outside the generator
for loop exits
Generator is now available for garbage collection
Generator is garbage collected
Generator's .close() is called
GeneratorExit is raised inside the generator
ValueError propagates to calling code
The last step does not occur until after your context manager has seen the GeneratorExit. When I run your code, I see the ValueError raised after the log message is printed.
You can see that the garbage collection is at work, because if you create another reference to the iterator itself, it will keep the iterator alive, so it won't be garbage collected, and so the GeneratorExit won't occur. That is, this "works":
it = iter(MyGenIt("foo",range(10)))
for x in it:
if x == 5:
raise ValueError("got 5")
The result is that the ValueError propagates and is visible; no GeneratorExit occurs and nothing is logged. You seem to think that the GeneratorExit is somehow "masking" your ValueError, but it isn't really; it's just an artifact introduced by not keeping any other references to the iterator. The fact that GeneratorExit occurs immediately in your example isn't even guaranteed behavior; it's possible that the iterator might not be garbage-collected until some unknown time in the future, and the GeneratorExit would then be logged at that time.
Turning to your larger question of "why do I see GeneratorExit", the answer is that that is the only exception that actually occurs within the generator function. The ValueError occurs outside the generator, so the generator can't catch it. This means your code can't really work in the way you seem to intend it to. Your with statement is inside the generator function. Thus it can only catch exceptions that happen in the process of yielding items from the generator; there generator has no knowledge of what happens between the times when it advances. But your ValueError is raised in the body of the loop over the generator contents. The generator is not executing at this time; it's just sitting there suspended.
You can't use a with statement in a generator to magically trap exceptions that occur in the code that iterates over the generator. The generator does not "know" about the code that iterates over it and can't handle exceptions that occur there. If you want to catch exceptions within the loop body, you need a separate with statement enclosing the loop itself.
The GeneratorExit is raised whenever a generator or coroutine is closed. Even without the context manager, we can replicate the exact condition with a simple generator function that prints out the exception information when it errors (further reducing the provided code to show exactly how and where that exception is generated).
import sys
def dummy_gen():
for idx in range(5):
try:
yield idx
except:
print(sys.exc_info())
raise
for i in dummy_gen():
raise ValueError('foo')
Usage:
(<class 'GeneratorExit'>, GeneratorExit(), <traceback object at 0x7f96b26b4cc8>)
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
ValueError: foo
Note there was also an exception that was raised inside the generator itself, as noted that the except block was executed. Note that the exception was also further raise'd after the print statement but note how that isn't actually shown anywhere, because it is handled internally.
We can also abuse this fact to see if we can manipulate the flow by swallowing the GeneratorExit exception and see what happens. This can be done by removing the raise statement inside the dummy_gen function to get the following output:
(<class 'GeneratorExit'>, GeneratorExit(), <traceback object at 0x7fd1f0438dc8>)
Exception ignored in: <generator object dummy_gen at 0x7fd1f0436518>
RuntimeError: generator ignored GeneratorExit
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
ValueError: foo
Note how there is an internal RuntimeError that was raised that complained about the generator ignoring the GeneratorExit function. So we from this we can clearly see that this exception is produced by the generator itself inside the generator function, and the ValueError that is raised outside that scope is never present inside the generator function.
Since a context manager will trap all exceptions as is, and the context manager is inside the generator function, whatever exception raised inside it will simply be passed to __exit__ as is. Consider the following:
class Context(object):
def __enter__(self):
return self
def __exit__(self, exc_type, exc_value, traceback):
if exc_type:
logging.error("Aborted %s", self,
exc_info=(exc_type, exc_value, traceback))
Modify the dummy_gen to the following:
def dummy_gen():
with Context():
for idx in range(5):
try:
yield idx
except:
print(sys.exc_info())
raise
Running the resulting code:
(<class 'GeneratorExit'>, GeneratorExit(), <traceback object at 0x7f44b8fb8908>)
ERROR:root:Aborted <__main__.Context object at 0x7f44b9032d30>
Traceback (most recent call last):
File "foo.py", line 26, in dummy_gen
yield idx
GeneratorExit
Traceback (most recent call last):
File "foo.py", line 41, in <module>
raise ValueError('foo')
ValueError: foo
The same GeneratorExit that is raised is now presented to the context manager, because this is the behavior that was defined.

Raising errors without traceback

I would like to use raise without printing the traceback on the screen. I know how to do that using try ..catch but doesn't find a way with raise.
Here is an example:
def my_function(self):
resp = self.resp
if resp.status_code == 404:
raise NoSuchElementError('GET'+self.url+'{}'.format(resp.status_code))
elif resp.status_code == 500:
raise ServerErrorError('GET'+self.url+'{}'.format(resp.status_code))
When executing this, if I have a 404, the traceback will print on the screen.
Traceback (most recent call last):
File "test.py", line 32, in <module>
print ins.my_function()
File "api.py", line 820, in my_function
raise NoSuchElementError('GET ' + self.url + ' {} '.format(resp.status_code))
This is an API wrapper and I don't want users to see the traceback but to see the API response codes and error messages instead.
Is there a way to do it ?
I ran into a similar problem where a parent class was using the exception value on raise to pass messages through but where I didn't want to dump the traceback. #lejlot gives a great solution using sys.excepthook but I needed to apply it with a more limited scope. Here's the modification:
import sys
from contextlib import contextmanager
#contextmanager
def except_handler(exc_handler):
"Sets a custom exception handler for the scope of a 'with' block."
sys.excepthook = exc_handler
yield
sys.excepthook = sys.__excepthook__
Then, to use it:
def my_exchandler(type, value, traceback):
print(': '.join([str(type.__name__), str(value)]))
with except_handler(my_exchandler):
raise Exception('Exceptional!')
# -> Exception: Exceptional!
That way, if an exception isn't raised in the block, default exception handling will resume for any subsequent exceptions:
with except_handler(my_exchandler):
pass
raise Exception('Ordinary...')
# -> Traceback (most recent call last):
# -> File "raise_and_suppress_traceback.py", line 22, in <module>
# -> raise Exception('Ordinary...')
# -> Exception: Ordinary...
The problem is not with raising anything, but with what python interpreter does, when your program terminates with an exception (and it simply prints the stack trace). What you should do if you want to avoid it, is to put try except block around everything that you want to "hide" the stack trace, like:
def main():
try:
actual_code()
except Exception as e:
print(e)
The other way around is to modify the exeption handler, sys.excepthook(type, value, traceback), to do your own logic, like
def my_exchandler(type, value, traceback):
print(value)
import sys
sys.excepthook = my_exchandler
you can even condition of exception type and do the particular logic iff it is your type of exception, and otherwise - backoff to the original one.
Modified #Alec answer:
#contextmanager
def disable_exception_traceback():
"""
All traceback information is suppressed and only the exception type and value are printed
"""
default_value = getattr(sys, "tracebacklimit", 1000) # `1000` is a Python's default value
sys.tracebacklimit = 0
yield
sys.tracebacklimit = default_value # revert changes
Usage:
with disable_exception_traceback():
raise AnyYourCustomException()
Use this if you only need to hide a traceback without modifying an exception message. Tested on Python 3.8
UPD: code improved by #DrJohnAStevenson comment
Catch the exception, log it and return something that indicates something went wrong to the consumer (sending a 200 back when a query failed will likely cause problems for your client).
try:
return do_something()
except NoSuchElementError as e:
logger.error(e)
return error_response()
The fake error_response() function could do anything form returning an empty response or an error message. You should still make use of proper HTTP status codes. It sounds like you should be returning a 404 in this instance.
You should handle exceptions gracefully but you shouldn't hide errors from clients completely. In the case of your NoSuchElementError exception it sounds like the client should be informed (the error might be on their end).
You can create a class that takes two values; Type and code for a custom Exception Message. Afterwards, you can just pass the class in a try/except statement.
class ExceptionHandler(Exception):
def __init__(self, exceptionType, code):
self.exceptionType = exceptionType
self.code = code
print(f"Error logged: {self.exceptionType}, Code: {self.code}")
try:
raise(ExceptionHandler(exceptionType=KeyboardInterrupt, code=101))
except Exception:
pass

python: raise("customException") - why no stack trace?

Using custom exceptions in python (v2.7.3): don't get a stack trace when call getSub() whereas getSub(True) invokes one, the difference being that it is caused via an additional try...except which I want to avoid (+feels unnecessary) so why/how can it be avoided?
import sys, traceback
class customException(Exception):
def __init__(self, *args):
super(customException,self).__init__(*args)
print "Stack trace within exception", traceback.extract_tb(sys.exc_info()[2])
errTxt = [a for a in args]
print "error text", errTxt
def getFn():
try:
getSub()
except customException as e:
print "customException was raised"
try:
getSub(True)
except customException as e:
print "customException2 was raised"
def getSub(flag=False):
if flag:
try:
1/0
except:
raise customException('test')
else:
raise customException('test')
getFn()
output:
Stack trace within exception []
error text ['test']
customException was raised
Stack trace within exception [('./test3.py', 25, 'getSub', '1/0')]
error text ['test']
customException2 was raised
To put the above in context I have code (pseudo) along the below lines and it wasn’t until I boiled down the code to the above example that I realised why the traceback access was not always working. The purpose of the custom exception class is to collect an aggregate count of exceptions in order to classify downstream the result of each do_something, e.g. fatal, warning etc. The use of the traceback was to record from WHERE the exception was ‘raised’ and therefore the creation of an exception (1/0 = albeit seems out of place) enables that to work. Wondering about using the inspect module rather than thinking about this within the traceback stack?
__main__
With each item in set:
Try:
do_something(item)
except customException()
clean up = log info etc.
end With
end.__main__
do_something(item)
try:
check_something()
except customException
if exception=typeA.1 raise customException(Type1)
if exception=typeB.2 and item = ‘x’
raise customException(Type2)
check_something()
a = getInfo()
unless getInfo()
raise customException(typeA.1)
try:
b = getOtherInfo()
except customException
raise customException(typeB.2)
…
If I understand you right, you're wondering why your print "Stack trace within exception" line prints an empty list for getSub(), but prints some traceback info for getSub(True).
You have code in your exception class's __init__ to look at the most recent exception via sys.exc_info. When you do raise customException('test'), customException('test') is evaluated first on its own, before it "knows" that it is going to be raised as an exception. So when you do that raise in getSub(), there is no most recent exception.
With getSub(True), there is a most recent exception, because the 1/0 raises an exception before you create your customException. Note that when you do 1/0, the most recent exception is the one from that 1/0; you can see that there's nothing about your customException in that traceback.
An exception object has to be created before the exception is raised. So you can't look at "the current exception" in your exception class's __init__ to get info about the stack trace that will be created when it is raised, because it hasn't been raised yet at that time.
If you want, you could use traceback.extract_stack to get the call stack at the time the exception object is created, but there's no guarantee that that has anything to do with when it will be raised. Just because an instance of your exception class is created doesn't even mean it will ever be raised at all. It's perfectly legal (although generally pointless) for someone to just create an exception object with stuff = customException('blah') but never actually raise the exception.
In any case, it's not clear from your question what you're trying to achieve here. It would help if you explained that.
Because you're catching the exception, there will be no traceback unless you either explicitly re-raise
try:
getSub(True)
except customException as e:
print "customException2 was raised"
raise # add this to re-raise, with original traceback
or print it yourself:
try:
getSub(True)
except customException as e:
print "customException2 was raised"
print traceback.format_exc()

Categories