I have been bitten by something unexpected recently. I wanted to make something like that:
try :
thing.merge(iterable) # this is an iterable so I add it to the list
except TypeError :
thing.append(iterable) # this is not iterable, so I add it
Well, It was working fine until I passed an object inheriting from Exception which was supposed to be added.
Unfortunetly, an Exception is iterable. The following code does not raise any TypeError:
for x in Exception() :
print 1
Does anybody know why?
Note that what is happening is not related to any kind of implicit string conversion etc, but because the Exception class implements __getitem__ to return the values from the args tuple (ex.args). You can see this by the fact that you get the whole string as your first and only item in the iteration, rather than the character-by-character result you'd get if you iterate over the string.
This surprised me too, but thinking about it, I'm guessing it is for backwards compatibility reasons. Python used to (pre-1.5) lack the current class hierarchy of exceptions. Instead, strings were thrown, with (usually) a tuple argument for any details that should be passed to the handling block, i.e:
try:
raise "something failed", (42, "some other details")
except "something failed", args:
errCode, msg = args
print "something failed. error code %d: %s" % (errCode, msg)
It looks like this behavior was put in to avoid breaking pre-1.5 code expecting a tuple of arguments, rather than a non-iterable exception object. There are a couple of examples of this with IOError in the Fatal Breakage section of the above link
String exceptions have been deprecated for a while, and are gone in Python 3. Exception objects are no longer iterable in Python 3:
>>> list(Exception("test"))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'Exception' object is not iterable
NOT VALID. Check Brian anwser.
Ok, I just got it :
for x in Exception("test") :
print x
....:
....:
test
Don't bother ;-)
Anyway, it's good to know.
EDIT : looking to the comments, I feel like adding some explanations.
An exception contains a message you passed to during instantiation :
raise Exception("test")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
Exception: test
It's fair to say that the message is what defines the Exception the best, so str() returns it :
print Exception("test")
test
Now, it happens that Exceptions are implicitly converted to string when used in something else than an Exception context.
So when I do :
for x in Exception("test") :
print x
I am iterating over the string "test".
And when I do :
for x in Exception() :
print x
I do iterate over an empty string. Tricky. Because when it comes to my issue :
try :
thing.merge(ExceptionLikeObject)
except TypeError :
...
This won't raise anything since ExceptionLikeObject is considered as a string.
Well now, we know the HOW, but I still not the WHY. Maybe the built-in Exception inherit from the built-in String ? Because as far as I know :
adding str does not make any object iterable.
I bypassed the problem by overloding iter, making it raising TypeError !
Not a problem anymore, but still a mystery.
Related
Greetings again StackOverflow Community,
I was reading through a library a colleague wrote and found something that I don't quite grasp what they are trying to do. But maybe this is something I am missing with respect to Python syntax.
class SampleClass:
def some_function(self) -> None:
try:
self.do_something()
except CustomException as e:
raise DifferentExceptionClass("Could not do something", e)
# The previous line is the cause of bewilderment.
def do_something(self) -> None:
raise CustomException("Tried to do something and failed.")
I've read that raise can accept arguments but this seems to raise DifferentExceptionClass exception with a tuple as the value. What is the difference between what my colleague has done here and doing something like raise DifferentExeptionClass("Could not do something. {}".format(e)) Is there a benefit to raising an exception his way?
The output to the function call to some_function() is:
test = SampleClass()
test.some_function()
Traceback (most recent call last):
File "<input>", line 4, in some_function
File "<input>", line 10, in do_something
CustomException: Tried to do something and failed.
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "<input>", line 1, in <module>
File "<input>", line 6, in some_function
DifferentExceptionClass: ('Could not do something', CustomException('Tried to do something and failed.',))
EDIT: The colleague is unreachable for comment. Also they wrote this library a long time ago and might not remember the "mood" they were in when they wrote this. I thought it would also make a good conversation on SO in case anyone else had seen similar implementation.
I've read that raise can accept arguments but this seems to raise DifferentExceptionClass exception with a tuple as the value.
Exceptions are in fact classes as well. Indeed somewhere you will find something like:
class DifferentExceptionClass(Exception):
def __init__(self,message,innerException):
# ...
pass
So you call a constructor. How the arguments are handled is up to the exception. It is possible that the message is formatted with the inner exception, but it is also possible that it does something totally different.
The advantage is for instance that the innerException (or other parameters) can be stored, inspected and handled accordingly. If you format the exception, the real exception data is lost: you only have a textual representation of it.
There's definitely benefit to doing this. You chain exceptions and provide that chain as information to the user rather than providing the most recent exception created.
Of course, your colleague could of done it in a better way by using syntax Python provides for this. The raise exc from raised_exc syntax is used to raise an exception after another exception has been raised:
except CustomException as e:
raise DifferentExceptionClass("Could not do something") from e
and causes e (the raised exception, CustomException here) to be stored as the __cause__ attribute of the most recent exception (DifferentExceptionClass here) if you need to peek at it.
In the case where a raise is used inside an except handler (as happens in your code snippet), the previous exception (e) is already implicitly stored as a __context__ attribute for it. So, passing it as an argument too, doesn't do anything else but also store the exception in the args tuple.
I do not want to do:
try:
pid = int(a_variable)
except StandardError as e:
pass
So, the question is what is the method of figuring out all of the possible exceptions can result in int(a_variable). Obviously, TypeError and ValueError can happen, but how do I work out what else can result?
You can pick the method that suits you from traceback module and combine it with a logger object (see logging module):
import traceback
import sys
import logging
try:
pid = int(sys.argv[1])
except Exception as e:
logging.error(traceback.print_exc())
Execution examples:
You can catch anIndexError exception with python begueradj.py
begueradj#begueradj:~/Desktop$ python begueradj.py
Traceback (most recent call last):
File "tester.py", line 6, in <module>
pid = int(sys.argv[1])
IndexError: list index out of range
You can catch a ValueError exception with python begueradj.py +:
begueradj#begueradj:~/Desktop$ python begueradj.py +
Traceback (most recent call last):
File "tester.py", line 6, in <module>
pid = int(sys.argv[1])
ValueError: invalid literal for int() with base 10: '+'
I only see two possible exceptions that can be generated by your code:
pid = int(sys.argv[1])
ValueError if the string in sys.argv[1] is no valid integer representation (like "one" or "1.5")
IndexError if the sys.argv list does not contain at least two elements, because the script was executed without command-line arguments.
There's no chance (assuming you don't override the builtins) that your code can raise a TypeError, because sys.argv is always a list of strings. Period.
You could eventually also even get a NameError if you forgot the import sys in your code above this line as well, but that's no runtime error but a programming error.
What could happen is that while your command is running the interpreter raises an unrelated exception like KeyboardInterrupt because the user pressed Ctrl+C in that exact millisecond - but you usually don't want to catch those locally.
As you're saying in your comment that the code line in your question was just an example, if we generalize this to int(x) (with x being a literal or a variable, but nothing that is able to throw exceptions itself, like method calls or operations), you can additionally get those exceptions:
TypeError if the argument is neither a numeric type (int, long, float, complex, bool), nor a string-like type (str, bytes, bytearray) nor any other object having a __int__ method or (since 3.4) a __index__ method.
NameError if you use a variable name as input which has not been defined yet in this context. However this is very unlikely unless you use eval or exec and let the user input a variable name (discouraged!). As this is a pure programming error and no runtime error and your IDE will highlight the use of undeclared variable names, it's usually not useful to check for this error.
I'm following along with the Python docs for try/except with the following effort:
def profile_check(self):
try:
profile = self.profile_type
return profile
except TypeError:
...
self.profile_type is a field in a Django model which doesn't exist in this case (therefore it returns as None). However, something seems to be missing, because it never moves on to the actions for except, rather it immediately throws the TypeError exception:
>>> a.profile_check()
Traceback (most recent call last):
File "<input>", line 1, in <module>
TypeError: 'NoneType' object is not callable
This is my first effort with try-catch, so I know it's something basic.
First off, your problem is that profile_check is set to None for some reason. That's why it's giving you that error when you call it.
As for your try/catch problem, if a variable doesn't exist, then the Python interpreter will throw a NameError at you. Try substituting TypeError for that.
I can't help you with your profile_check-is-None situation though without anymore context though. Sorry!
This is a very silly question, but I'm running some tasks and catching their errors by:
try:
run_something()
except Exception as e:
handle_error(str(e))
I want the error message as a String because I'm using a UI and I want to display the error in a window.
The problem can be replicated as:
>>> import numpy as np
>>> np.range(1e10)
MemoryError Traceback (most recent call last)
<ipython-input-4-20a59c2069b2> in <module>()
----> 1 np.arange(1e10)
MemoryError:
However, if I try to catch the error and print its message (which I was hoping to be something like "MemoryError":
try:
np.arange(1e10)
except Exception as e:
print(str(e))
print("Catched!")
The only output I get is "Catched!". This is so stupid, I was doing some work with UI's and threading, and took me a while to realise that the problem was a memory error with no message at all.
Is the MemoryError the only Exception that is translated to an empty string? Because if it is the case I can check it. If not, how to get its message as a String?
So you probably want to print the name of the exception class:
try:
np.arange(1e10)
except Exception as e: #not catch...
print(str(e.__class__.__name__))
print("Catched!")
Using str(e) only prints the "message" of the exception, which in your case was empty.
Note that you can obtain the arguments passed to the exception constructor via the args attribute:
In [4]: try:
...: raise ValueError(1,2,3)
...: except ValueError as e:
...: print('arguments:', e.args)
arguments: (1, 2, 3)
When call str(e) it returns the message of the Exception. Take an example -
NameError: name 'a' is not defined
^ ^
name message
The part before : is the name of the exception, whereas the part after it is the message (arguments).
In the case of MemoryError , as you can see in your example -
MemoryError:
does not have an error message , only the name of the exception , hence you get the empty string.
I am not sure if there are other Exceptions that do not have an exception, but I believe it would be very rare to find such exceptions. Maybe you can print both the exception's name as well as the message , if you really want to take care of the MemoryError (and maybe other such rare exceptions without messages) , something like -
print(type(e).__name__ , ':', str(e))
From the Python documentation (Version 2.7, I guess also applies to Python 3.x):
The base class for all built-in exceptions. It is not meant to be directly inherited by user-defined classes (for that, use Exception). If str() or unicode() is called on an instance of this class, the representation of the argument(s) to the instance are returned, or the empty string when there were no arguments.
It seems, that MemoryError gets no arguments, and thus according to this documentation does give an empty string back.
The exception can of course still be catched, because it can be catched by its type.
You can get the name of the exception class and print that.
This makes of course also a lot of sense, since look at that:
a = {}
try:
a['barrel']
except Exception as e:
print str(e)
Will print just 'barrel' -- what is not so helpful, so to add the class name of the exception is really a good idea:
...
except Exception as e:
print(e.__class__.__name + ': ' + (str(e)))
I was trying to convert the list to a set, with the following code:
set1=set(list1)
the code was running fine, but all on a sudden started to give the following error,
set1=set(list1)
TypeError: unhashable type: 'list'
please let me know how may I resolve.
And sometimes some good running program gives error all on a sudden with no parameter changed, how may I debug it?
Your error suggests that your list contains a list. Lists are mutable and thus can't be hashed for use in a set or a dictionary. One work-around is to convert your list into a tuple using tuple(some_list), but if they're heavily nested, it becomes more complex.
Your list contains another list:
>>> set([['contained list'], 1, 2])
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unhashable type: 'list'
Sets can only contain items that are hashable; any standard type that cannot be mutated is hashable, but a list is not (it can be mutated). By contrast, a tuple is not mutable and can be stored in a set.
Bugs happen, even in code that has been running fine for a while. Debug it with print statements, or better still, by using a debugger like the pdb.
If your bug only appears intermittently, use a try/except block to catch the error, then print out information or use a debugger to figure out what is going on:
try:
set1=set(list1)
except TypeError:
print 'list1 not hashable? contents: %r' % list1
# optionally: import pdb; pdb.set_trace()
raise