Handle exception in __init__
Question:
Is it fine to raise an exception in __init__
in python? I have this piece of code:
class VersionManager(object):
def __init__(self, path):
self._path = path
if not os.path.exists(path): os.mkdir(path)
myfunction(path)
The second line can potentially result in an exception. In that case the object will not be init’ed properly. Is there a better way to handle situations where code in __init__
might throw an exception?
EDIT
Added a call to a function after os.mkdir
Added a check to see if directory exists
Answers:
You can use try/except when initializing the object.
try:
ver = VersionManager(my_path)
except Exception as e:
# raise e or handle error
print e
My favourite is to simply output errors to console and march on:
import sys, os, traceback
class Myclass
def __init__(self, path):
self._path = path
"""Risky Code"""
try:
os.mkdir(path)
except:
traceback.print_exc(file = sys.stdout)
This way an exception will print out more like a warning rather than a real exception.
You can wrap the call, as jramirez suggested:
try:
ver = VersionManager(path)
except:
raise
Or you can use a context manager:
class VersionManager(object):
def __init__(self):
#not-so-harmful code
self.path = path
def __enter__(self):
try:
self.path = path
os.mkdir(path)
self.myfunction(path)
except Exception as e:
print e
print "The directory making has failed, the function hasn't been executed."
return self
def __exit__(self, exc_type, exc_value, traceback):
print(exc_type, exc_value, traceback)
And to run it:
with VersionManager(my_path) as myVersionManager:
#do things you want with myVersionManager
This way, you’ll catch errors inside the with
statement as well.
It is perfectly fine to raise an exception in __init__
. You would then wrap the object initiation/creation call with try/except
and react to the exception.
One potential odd result though is that __del__
is run anyway:
class Demo(object):
def __init__(self, value):
self.value=value
if value==2:
raise ValueError
def __del__(self):
print '__del__', self.value
d=Demo(1) # successfully create an object here
d=22 # new int object labeled 'd'; old 'd' goes out of scope
# '__del__ 1' is printed once a new name is put on old 'd'
# since the object is deleted with no references
Now try with the value 2
that we are testing for:
Demo(2)
Traceback (most recent call last):
File "Untitled 3.py", line 11, in <module>
Demo(2)
File "Untitled 3.py", line 5, in __init__
raise ValueError
ValueError
__del__ 2 # But note that `__del__` is still run.
The creation of the object with value 2
raises a ValueError
exception and show that __del__
is still run to clean up the object.
Keep in mind that if you raise an exception during __init__
your object will not get a name. (It will, however, be created and destroyed. Since __del__
is paired with __new__
it still gets called)
ie, just like this does not create x
:
>>> x=1/0
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ZeroDivisionError: integer division or modulo by zero
>>> x
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'x' is not defined
Potential sneakier:
>>> x='Old X'
>>> x=1/0
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero
>>> x
'Old X'
Same thing if you catch an exception of __init__
:
try:
o=Demo(2)
except ValueError:
print o # name error -- 'o' never gets bound to the object...
# Worst still -- 'o' is its OLD value!
So don’t try to refer to the incomplete object o
— it’s gone out of scope by the time you get to except
. And the name o
is either nothing (i.e., NameError
if you try to use it) or its old value.
So wrapping up (thanks to Steve Jessop for the User Defined Exception idea), you can wrap the creation of the object and catch the exception. Just figure out how to react appropriately to the OS error you are looking at.
So:
class ForbiddenTwoException(Exception):
pass
class Demo(object):
def __init__(self, value):
self.value=value
print 'trying to create with val:', value
if value==2:
raise ForbiddenTwoException
def __del__(self):
print '__del__', self.value
try:
o=Demo(2)
except ForbiddenTwoException:
print 'Doh! Cant create Demo with a "2"! Forbidden!!!'
# with your example - react to being unusable to create a directory...
Prints:
trying to create with val: 2
Doh! Cant create Demo with a "2"! Forbidden!!!
__del__ 2
Is it fine to raise an exception in __init__
in python? I have this piece of code:
class VersionManager(object):
def __init__(self, path):
self._path = path
if not os.path.exists(path): os.mkdir(path)
myfunction(path)
The second line can potentially result in an exception. In that case the object will not be init’ed properly. Is there a better way to handle situations where code in __init__
might throw an exception?
EDIT
Added a call to a function after os.mkdir
Added a check to see if directory exists
You can use try/except when initializing the object.
try:
ver = VersionManager(my_path)
except Exception as e:
# raise e or handle error
print e
My favourite is to simply output errors to console and march on:
import sys, os, traceback
class Myclass
def __init__(self, path):
self._path = path
"""Risky Code"""
try:
os.mkdir(path)
except:
traceback.print_exc(file = sys.stdout)
This way an exception will print out more like a warning rather than a real exception.
You can wrap the call, as jramirez suggested:
try:
ver = VersionManager(path)
except:
raise
Or you can use a context manager:
class VersionManager(object):
def __init__(self):
#not-so-harmful code
self.path = path
def __enter__(self):
try:
self.path = path
os.mkdir(path)
self.myfunction(path)
except Exception as e:
print e
print "The directory making has failed, the function hasn't been executed."
return self
def __exit__(self, exc_type, exc_value, traceback):
print(exc_type, exc_value, traceback)
And to run it:
with VersionManager(my_path) as myVersionManager:
#do things you want with myVersionManager
This way, you’ll catch errors inside the with
statement as well.
It is perfectly fine to raise an exception in __init__
. You would then wrap the object initiation/creation call with try/except
and react to the exception.
One potential odd result though is that __del__
is run anyway:
class Demo(object):
def __init__(self, value):
self.value=value
if value==2:
raise ValueError
def __del__(self):
print '__del__', self.value
d=Demo(1) # successfully create an object here
d=22 # new int object labeled 'd'; old 'd' goes out of scope
# '__del__ 1' is printed once a new name is put on old 'd'
# since the object is deleted with no references
Now try with the value 2
that we are testing for:
Demo(2)
Traceback (most recent call last):
File "Untitled 3.py", line 11, in <module>
Demo(2)
File "Untitled 3.py", line 5, in __init__
raise ValueError
ValueError
__del__ 2 # But note that `__del__` is still run.
The creation of the object with value 2
raises a ValueError
exception and show that __del__
is still run to clean up the object.
Keep in mind that if you raise an exception during __init__
your object will not get a name. (It will, however, be created and destroyed. Since __del__
is paired with __new__
it still gets called)
ie, just like this does not create x
:
>>> x=1/0
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ZeroDivisionError: integer division or modulo by zero
>>> x
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
NameError: name 'x' is not defined
Potential sneakier:
>>> x='Old X'
>>> x=1/0
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ZeroDivisionError: division by zero
>>> x
'Old X'
Same thing if you catch an exception of __init__
:
try:
o=Demo(2)
except ValueError:
print o # name error -- 'o' never gets bound to the object...
# Worst still -- 'o' is its OLD value!
So don’t try to refer to the incomplete object o
— it’s gone out of scope by the time you get to except
. And the name o
is either nothing (i.e., NameError
if you try to use it) or its old value.
So wrapping up (thanks to Steve Jessop for the User Defined Exception idea), you can wrap the creation of the object and catch the exception. Just figure out how to react appropriately to the OS error you are looking at.
So:
class ForbiddenTwoException(Exception):
pass
class Demo(object):
def __init__(self, value):
self.value=value
print 'trying to create with val:', value
if value==2:
raise ForbiddenTwoException
def __del__(self):
print '__del__', self.value
try:
o=Demo(2)
except ForbiddenTwoException:
print 'Doh! Cant create Demo with a "2"! Forbidden!!!'
# with your example - react to being unusable to create a directory...
Prints:
trying to create with val: 2
Doh! Cant create Demo with a "2"! Forbidden!!!
__del__ 2