Use an object even though `__init__()` raises an exception? - python

I'm in a situation where some meager important parts of a classes __init__ method could raise an exception. In that case I want to display an error message but carry on using the instance.
A very basic example:
class something(object):
def __init__(self):
do_something_important()
raise IrrelevantException()
def do_something_useful(self):
pass
try:
that_thing = something()
except IrrelevantException:
print("Something less important failed.")
that_thing.do_something_useful()
However, the last line does not work, because that_thing is not defined. Strange thing is, I could swear I've done things like this before and it worked fine. I even thougt about ways to keep people from using such an unfinished instance, because I found out it gets created even in case of errors. Now I wanted to use that and it does not work. Hmmm...?!?
PS: something was written by myself, so I'm in control of everything.

You can accomplish this by calling object.__new__() to create the object. Then after that call __init__() to create the object.
This will execute all of the code possible.
class IrrelevantException(Exception):
"""This is not important, keep executing."""
pass
class something(object):
def __init__(self):
print("Doing important stuff.")
raise IrrelevantException()
def do_something_useful(self):
print("Now this is useful.")
that_thing = object.__new__(something) # Create the object, does not call __init__
try:
that_thing.__init__() # Now run __init__
except IrrelevantException:
print("Something less important failed.")
that_thing.do_something_useful() # And everything that __init__ could do is done.
EDIT, as #abarnert pointed out. This code does presume that __init__() is defined, but __new__() is not.
Now if it can be assumed that __new__() will not error, it can replace object.__new__() in the code.
However, if there is an error in object.__new__(), there is no way to both create the instance, and have the actions in __new__() applied to it.
This is because __new__() returns the instance, versus __init__() which manipulates the instance. (When you call something(), the default __new__() function actually calls __init__() and then quietly returns the instance.)
So the most robust version of this code would be:
class IrrelevantException(Exception):
"""This is not important, keep executing."""
pass
class something(object):
def __init__(self):
print("Doing important stuff.")
raise IrrelevantException()
def do_something_useful(self):
print("Now this is useful.")
try:
that_thing = something.__new__(something) # Create the object, does not call __init__
except IrrelevantException:
# Well, just create the object without calling cls.__new__()
that_thing = object.__new__(something)
try:
that_thing.__init__() # Now run __init__
except IrrelevantException:
print("Something less important failed.")
that_thing.do_something_useful()
So, meanwhile both of these answer the question, this latter one should also help in the (admittedly rare) case where __new__() has an error, but this does not stop do_something_useful() from working.

From a comment:
PS: something was written by myself, so I'm in control of everything.
Well, then the answer is obvious: just remove that raise IrrelevantException()
Of course your real code probably doesn't have raise IrrelevantException, but instead a call to some dangerous_function() that might raise. But that's fine; you can handle the exception the same way you do anywhere else; the fact that you're inside an __init__ method makes no difference:
class something(object):
def __init__(self):
do_something_important()
try:
do_something_dangerous()
except IrrelevantException as e:
print(f'do_something_dangerous raised {e!r}')
do_other_stuff_if_you_have_any()
That's all there is to it. There's no reason your __init__ should be raising an exception, and therefore the question of how to handle that exception never arises in the first place.
If you can't modify something, but can subclass it, then you don't need anything fancy:
class IrrelevantException(Exception):
pass
def do_something_important():
pass
class something(object):
def __init__(self):
do_something_important()
raise IrrelevantException()
def do_something_useful(self):
pass
class betterthing(something):
def __init__(self):
try:
super().__init__() # use 2.x style if you're on 2.x of course
except IrrelevantException:
pass # or log it, or whatever
# You can even do extra stuff after the exception
that_thing = betterthing()
that_thing.do_something_useful()
Now do_something_important got called, and a something instance got returns that I was able to save and call do_something_useful on, and so on. Exactly what you were looking for.
You could of course hide something behind betterthing with some clever renaming tricks:
_something = something
class something(_something):
# same code as above
… or just monkeypatch something.__init__ with a wrapper function instead of wrapping the class:
_init = something.__init__
def __init__(self):
try:
_init(self)
except IrrelevantException:
pass
something.__init__ = __init__
But, unless there's a good reason that you can't be explicit about the fact that you're adding a wrapper it, it's probably better to be explicit.

You can't have both an exception raised and a value returned (without getting hacky). If this is all code you control, then may I suggest this pattern:
class something(object):
Exception = None
def __init__(self):
...
if BadStuff:
self.Exception = IrrelevantException()
...
that_thing = something()
if that_thing.Exception:
print(that_thing.Exception)
# carry on
Note, if you are just looking for a message, then don't bother creating an Exception object, but rather just set an error code/message on self, and check for it later.

I assume that you don't have control over the "something" class, so in that case you can call the method directly, assuming that there are no elements in the class that are needed. You're passing self=None though, so it won't be able to have any access to the class's variables.
class IrrelevantException(Exception):
x = "i don't matter"
class something(object):
def __init__(self):
raise IrrelevantException()
def do_something_useful(self):
print('hi')
#this will fail
try:
that_thing = something()
except IrrelevantException:
print("Something less important failed.")
#this will run
something.do_something_useful(None)
Alternatively you can use inheritance:
class mega_something(something):
def __init__(self):
print("its alive!")
that_other_thing = mega_something()
that_other_thing.do_something_useful()
The mega_something class won't run its parent constructor unless called.

Related

Why this test fails?

After adding a new unit test I started to get failures in unrelated test run after the new test. I could not understand why.
I have simplified the case to the code below. I still do not understand what is going on. I am surprised that commenting out seemingly unrelated lines of code affects the result: removing the call to isinstance in Block.__init__ changes the result of isinstance(blk, AddonDefault) in test_addons.
import abc
class Addon:
pass
class AddonDefault(Addon, metaclass=abc.ABCMeta):
pass
class Block:
def __init__(self):
isinstance(self, CBlock)
class CBlock(Block, metaclass=abc.ABCMeta):
def __init_subclass__(cls, *args, **kwargs):
if issubclass(cls, Addon):
raise TypeError("Do not mix Addons and CBlocks!")
super().__init_subclass__(*args, **kwargs)
class FBlock(CBlock):
pass
def test_addons():
try:
class CBlockWithAddon(CBlock, AddonDefault):
pass
except TypeError:
pass
blk = FBlock()
assert not isinstance(blk, AddonDefault), "TEST FAIL"
print("OK")
test_addons()
When I run python3 test.py I get the TEST FAIL exception. But FBlock is derived from CBlock which is derived from Block. How can it be an instance of AddonDefault?
UPDATE: I'd like to emphasize that the only purpose of the posted code is to demonstrate the behaviour I cannot understand. It was created by reducing a much larger program as much as I was able to. During this process any logic it had before was lost, so please take it as it is and focus on the question why it gives an apparantly incorrect answer.
Not a full answer, but some hints.
It seems that CBlockWithAddon is still seen as a subclass of AddonDefault. E.g. add two print statements to your test_addons():
def test_addons():
print(AddonDefault.__subclasses__())
try:
class CBlockWithAddon(CBlock, AddonDefault):
pass
except TypeError:
pass
print(AddonDefault.__subclasses__())
blk = FBlock()
assert not isinstance(blk, AddonDefault), "TEST FAIL"
print("OK")
results in
[]
[<class '__main__.test_addons.<locals>.CBlockWithAddon'>]
...
AssertionError: TEST FAIL
_py_abc tests for this:
# Check if it's a subclass of a subclass (recursive)
for scls in cls.__subclasses__():
if issubclass(subclass, scls):
cls._abc_cache.add(subclass)
return True
This will return True when cls=AddonDefault, subclass=FBlock and scls=CBlockWithAddon.
So it seems two things are going wrong:
The improperly created CBlockWithAddon is still seen as a subclass of AddonDefault.
But CBlockWithAddon is somehow created such that it seems to be a superclass of FBlock.
Perhaps the broken CBlockWithAddon is effectively identical to CBlock, and is therefore a superclass of FBlock.
This is enough for me now. Maybe it helps your investigation.
(I had to use import _py_abc as abc for this analysis. It doesn't seem to matter.)
Edit1: My hunch about CBlockWithAddon resembling its superclass CBlock seems correct:
CBWA = AddonDefault.__subclasses__()[0]
print(CBWA)
print(CBWA.__dict__.keys())
print(CBlock.__dict__.keys())
print(CBWA._abc_cache is CBlock._abc_cache)
gives
<class '__main__.test_addons.<locals>.CBlockWithAddon'>
dict_keys(['__module__', '__doc__'])
dict_keys(['__module__', '__init_subclass__', '__doc__', '__abstractmethods__', '_abc_registry', '_abc_cache', '_abc_negative_cache', '_abc_negative_cache_version'])
True
So CBlockWithAddon is not properly created, e.g. its cache registry is not properly set. So accessing those attributes will access those of the (first) super class, in this case CBlock. The not-so dummy isinstance(self, CBlock) will populate the cache when blk is created, because FBlock is indeed a subclass of CBlock. This cache is then incorrectly reused when isinstance(blk, AddonDefault) is called.
I think this answers the question as is. Now the next question would be: why does CBlockWithAddon become a subclass of CBlock when it was never properly defined?
Edit2: Simpler Proof of Concept.
from abc import ABCMeta
class Animal(metaclass=ABCMeta):
pass
class Plant(metaclass=ABCMeta):
def __init_subclass__(cls):
assert not issubclass(cls, Animal), "Plants cannot be Animals"
class Dog(Animal):
pass
try:
class Triffid(Animal, Plant):
pass
except Exception:
pass
print("Dog is Animal?", issubclass(Dog, Animal))
print("Dog is Plant?", issubclass(Dog, Plant))
will result in
Dog is Animal? True
Dog is Plant? True
Note that changing the order of the print statements will result in
Dog is Plant? False
Dog is Animal? False
Why are you making sub classes abstract instead of the base classes?
Is there some kind of logic behind this?
If you move abstraction one layer up it works as intended otherwise you mix type and abc metaclasses:
import abc
class Addon(metaclass=abc.ABCMeta):
pass
class AddonDefault(Addon):
pass
class Block(metaclass=abc.ABCMeta):
def __init__(self):
isinstance(self, CBlock)
class CBlock(Block):
def __init_subclass__(cls, *args, **kwargs):
if issubclass(cls, Addon):
raise TypeError("Do not mix Addons and CBlocks!")
super().__init_subclass__(*args, **kwargs)
class FBlock(CBlock):
pass
def test_addons():
try:
class CBlockWithAddon(CBlock, AddonDefault):
pass
except TypeError:
pass
blk = FBlock()
assert not isinstance(blk, AddonDefault), "TEST FAIL"
print("OK")
test_addons()

Object that raises exception when used in any way

I need to create an object that would raise a custom exception, UnusableObjectError, when it is used in any way (creating it should not create an exception though).
a = UnusableClass() # No error
b = UnusableClass() # No error
a == 4 # Raises UnusableObjectError
'x' in a # Raises UnusableObjectError
for i in a: # Raises UnusableObjectError
print(i)
# ..and so on
I came up with the code below which seems to behave as expected.
class UnusableObjectError(Exception):
pass
CLASSES_WITH_MAGIC_METHODS = (str(), object, float(), dict())
# Combines all magic methods I can think of.
MAGIC_METHODS_TO_CHANGE = set()
for i in CLASSES_WITH_MAGIC_METHODS:
MAGIC_METHODS_TO_CHANGE |= set(dir(i))
MAGIC_METHODS_TO_CHANGE.add('__call__')
# __init__ and __new__ must not raise an UnusableObjectError
# otherwise it would raise error even on creation of objects.
MAGIC_METHODS_TO_CHANGE -= {'__class__', '__init__', '__new__'}
def error_func(*args, **kwargs):
"""(nearly) all magic methods will be set to this function."""
raise UnusableObjectError
class UnusableClass(object):
pass
for i in MAGIC_METHODS_TO_CHANGE:
setattr(UnusableClass, i, error_func)
(some improvements made, as suggested by Duncan in comments)
Questions:
Is there an already existing class that behaves as described?
If not, is there any flaw in my UnusableClass() (e.g., situations when using the instances of the class wouldn't raise an error) and if so, how can I fix those flaws?
Turns out metaclasses and dunder (double underscore) methods don't go well together (which is unfortunate, since that would have been a more streamlined way to implement this).
I couldn't find any importable listing of magic method names, so I created one and put it on PyPi (https://pypi.python.org/pypi/magicmethods/0.1.1). With it, the implementation of UnusableClass can be written as a simple class decorator:
import magicmethods
class UnusableObjectError(Exception):
pass
def unusable(cls):
def _unusable(*args, **kwargs):
raise UnusableObjectError()
for name in set(magicmethods.all) - set(magicmethods.lifecycle):
setattr(cls, name, _unusable)
return cls
#unusable
class UnusableClass(object):
pass
magicmethods.lifecycle contains __new__, __init__, and __del__. You might want to adjust this..
This implementation also handles:
a = UnusableClass()
with a:
print 'oops'
You can use __getattribute__ to block all access to attributes, except special __ attributes like __contains__ or __eq__ which are not catched by __getattribute__, and use a whitelist to allow access to some methods:
class UnuseableClass(object):
whitelist = ('alpha', 'echo',)
def __init__(self):
self.alpha = 42
def echo(self, text):
print text
def not_callable(self):
return 113
def __getattribute__(self, name):
if name in type(self).whitelist:
return super(UnuseableClass, self).__getattribute__(name)
else:
raise Exception('Attribute is not useable: %s' % name)
unuseable_object = UnuseableClass()
print(unuseable_object.alpha)
unuseable_object.echo('calling echo')
try:
unuseable_object.not_callable()
except Exception as exc:
print(exc.message)
If you really need to catch even special method calls, you can use How to catch any method called on an object in python?.

Confused about try/except with custom Exception

My code:
class AError(Exception):
print 'error occur'
for i in range(3):
try:
print '---oo'
raise AError
except AError:
print 'get AError'
else:
print 'going on'
finally:
print 'finally'
When I run the above code, the output is this:
error occur
---oo
get AError
finally
---oo
get AError
finally
---oo
get AError
finally
I think the string "error occur" should occur three times, like "---oo", but it only occurs once; why?
To clarify Paul's answer, here's a simple example:
class Test(object):
print "Class being defined"
def __init__(self):
print "Instance being created"
for _ in range(3):
t = Test()
The output from this will be:
Class being defined
Instance being created
Instance being created
Instance being created
Code within the class definition but outside a method definition runs only once, when the class is defined.
If you want code to run whenever an instance is created, it should be in the __init__ instance method (or, occasionally, the __new__ class method). However, note that if you define __init__ for a subclass, you should probably ensure that it also calls the superclass's __init__:
class AError(Exception):
def __init__(self, *args, **kwargs):
Exception.__init__(self, *args, **kwargs) # call the superclass
print 'error occur' # print your message
This ensures that the subclass supports the arguments for the superclass; in the case of Exception, you can e.g. pass an error message:
>>> raise AError("Something went wrong.")
error occur # your message gets printed when the instance is created
Traceback (most recent call last):
File "<pyshell#11>", line 1, in <module>
raise AError("Something went wrong.")
AError: Something went wrong. # the error message passes through to the traceback
For an explanation of the *args, **kwargs syntax, if you aren't familiar with it, see e.g. What does ** (double star) and * (star) do for parameters?. You can also use super to call the superclass methods, see e.g. Understanding Python super() with __init__() methods.
'error occur' only gets printed once, for the entire class.
You probably expected it to get run for each instance of the class that was created.
For that to happen, put it in the __init__ function,
class AError(Exception):
def __init__(self):
print 'error occur'
__init__ is called when an instance of AError is created.
I strongly recommend not to place any print statements in your Exception, esp. not their constructors! Exceptions are semantic entities and you can print them out if you need to. If you must automate the printing at least use logging or a similar package.
What you might not be aware of is that you can collect the exception instance for use in the except clause like so:
class MyError(Exception):
pass
for i in range(3):
try:
print '---oo'
raise MyError("error msg no. {}".format(i))
# Exception usually always accept a string as their first parameter
except MyError, ex:
# Exception instance available inside the except clause as `ex`
print ex
else:
print 'going on'
finally:
print 'finally'
The 'error occur' string appears only one because Python executes it when parsing your AError class definition.
If you want to get it executed every time you create an instance of your class, you must define the class's initialiser:
class AError(Exception):
def __init__(self):
print 'error occur'
for i in range(3):
try:
print '---oo'
raise AError
except AError:
print 'get AError'
else:
print 'going on'
finally:
print 'finally'
Have fun (and read the language manual maybe...)

Python magic method to alter the way raising an object is handled

I was wondering, is there a simple magic method in python that allows customization of the behaviour of an exception-derived object when it is raised? I'm looking for something like __raise__ if that exists. If no such magic methods exist, is there any way I could do something like the following (it's just an example to prove my point):
class SpecialException(Exception):
def __raise__(self):
print('Error!')
raise SpecialException() #this is the part of the code that must stay
Is it possible?
I don't know about such magic method but even if it existed it is just some piece of code that gets executed before actually raising the exception object. Assuming that its a good practice to raise exception objects that are instantiated in-place you can put such code into the __init__ of the exception. Another workaround: instead of raising your exception directly you call an error handling method/function that executes special code and then finally raises an exception.
import time
from functools import wraps
def capture_exception(callback=None, *c_args, **c_kwargs):
"""捕获到异常后执行回调函数"""
assert callable(callback), "callback 必须是可执行对象"
def _out(func):
#wraps(func)
def _inner(*args, **kwargs):
try:
res = func(*args, **kwargs)
return res
except Exception as e:
callback(*c_args, **c_kwargs)
raise e
return _inner
return _out
def send_warning():
print("warning message..............")
class A(object):
#capture_exception(callback=send_warning)
def run(self):
print('run')
raise SystemError("测试异常捕获回调功能")
time.sleep(0.2)
if __name__ == '__main__':
a = A()
a.run()

How to make sure a class function wont be called until another class function has been called first?

I have a class object that creates some data fields:
class DataFields(object):
_fields_ = ['field_1', 'field_2', 'data_length']
def __init__(self, data=None):
if data != None:
self.create_fields(data)
def create_fields(self, data):
i = 0
for field in self._fields_:
setattr(self, field, data[i])
i += 1
def get_datalength(self):
return self.data_length
What is the best way to make sure that the get_datalength() function cannot be called unless the data_length field has been created (that is, unless the create_fields() function has been called once).
I've thought about either using a variable that gets initialized in the create_fields and is checked in get_datalength() or try-except inside the get_datalength() function. What is the most Pythonic (or the best) way?
I think the most pythonic way would be to throw an exception:
def get_datalength(self):
try:
return self.data_length
except AttributeError:
raise AttributeError("No length call create_fields first")
Simple reason: There is no way to prevent the user to call this function on the object. Either the user would get a AttributeError and would not understand what is going on, or you provide an own Error class or at least error message.
BTW:
It is not pythonic creating getter methods(there are no such things as 'private members')
If you need to do smaller operation on the value returning it have a look at the #property decorator
#property
def datalength(self):
return do_some_stuff(self.data_length)
By using getattr with default value, you can return None or any value if there is no data_length attribute yet in instance:
def get_datalength(self):
return getattr(self, 'data_length', None)
Using an exception is probably the best way for what you are doing however there are alternatives that may be useful if you will be using this object from an interactive console:
def fn2(self):
print("this is fn2")
class test:
def fn1(self):
print("this is fn1")
self.fn2 = fn2
def fn2(self): # omit this if you want fn2 to only exist after fn1 is called
print("Please call fn1 first")
I wouldn't recommend this for every-day use but it can be useful in some cases. If you omit defining fn2 within the class, then the method fn2 will only be present after fn1 is called. For easier code maintenance you can do the same thing like this:
class test:
def fn1(self):
print("this is fn1")
self.fn2 = self._fn2
def _fn2(self):
print("this is fn2")
def fn2(self): # omit this if you want fn2 to only exist after fn1 is called
print("Please call fn1 first")
If this is to be used inside a module that will be imported then you should either raise an exception or return a valid value like the other answers have suggested.
This can be solved by having a dictionary, as a class variable, with method names as keys.
called['method1']
called['method2']
called['method3']
...
And setting the key in that method call
class SomeClass(obj):
def method1():
called['method1'] = 1
def method2():
if method1 in called:
# continue

Categories

Resources