Question

I got the following class :

class ConstraintFailureSet(dict, Exception) :
    """
        Container for constraint failures. It act as a constraint failure itself
        but can contain other constraint failures that can be accessed with a dict syntax.
    """

    def __init__(self, **failures) :
        dict.__init__(self, failures)
        Exception.__init__(self)

print isinstance(ConstraintFailureSet(), Exception)
True
raise ConstraintFailureSet()
TypeError: exceptions must be classes, instances, or strings (deprecated), not ConstraintFailureSet

What the heck ?

And the worst is that I can't try super() since Exception are old based class...

EDIT : And, yes, I've tried to switch the order of inheritance / init.

EDIT2 : I am using CPython 2.4 on Ubuntu8.10. You newer know is this kind of infos is usefull ;-). Anyway, this little riddle has shut the mouth of 3 of my collegues. You'd be my best-friend-of-the day...

Was it helpful?

Solution

Both Exception and dict are implemented in C.

I think you can test this the follwing way:

>>> class C(object): pass
...
>>> '__module__' in C.__dict__
True
>>> '__module__' in dict.__dict__
False
>>> '__module__' in Exception.__dict__
False

Since Exception and dict have different ideas of how to store their data internally, they are not compatible and thus you cannot inherit from both at the same time.

In later versions of Python you should get an Exception the moment you try to define the class:

>>> class foo(dict, Exception):
...     pass
... 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Error when calling the metaclass bases
    multiple bases have instance lay-out conflict

OTHER TIPS

What's wrong with this?

class ConstraintFailure( Exception ):
    def __init__( self, **failures ):
        self.failures= failures # already a dict, don't need to do anything
    def __getitem__( self, key ):
        return self.failures.get(key)

This is an Exception, and it contains other exceptions in an internal dictionary named failures.

Could you update your problem to list some some specific thing this can't do?

try:
    raise ConstraintFailure( x=ValueError, y=Exception )
except ConstraintFailure, e:
    print e['x']
    print e['y']


<type 'exceptions.ValueError'>
<type 'exceptions.Exception'>

No reason but a solution

For the moment I still don't know the why, but I bypass it using UserDict.UserDict. It's slower since it's pure Python, but I don't think on this part of the app it will be troublesome.

Still interested about the answer anyway ;-)

What version of Python?

In 2.5.1, I can't even define a class the inherits from both dict and Exception:

>>> class foo(dict, Exception):
...   pass
... 
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Error when calling the metaclass bases
    multiple bases have instance lay-out conflict

If you're using an older version, maybe it doesn't do this check during type definition and the conflict causes oddities later on.

I am almost certain that with 2.4 problem is caused by exceptions being old style classes.

$ python2.4
Python 2.4.4 (#1, Feb 19 2009, 09:13:34)
>>> type(dict)
<type 'type'>
>>> type(Exception)
<type 'classobj'>
>>> type(Exception())
<type 'instance'>

$ python2.5
Python 2.5.4 (r254:67916, Feb 17 2009, 23:11:16)
>>> type(Exception)
<type 'type'>
>>> type(Exception())
<type 'exceptions.Exception'>

In both versions as the message says exceptions can be classes, instances (of old style classes) or strings (deprecated).

From version 2.5 exception hierarchy is based on new style classes finally. And instances of new style classes which inherit from BaseException are now allowed too. But in 2.4 multiple inheritance from Exception (old style class) and dict (new style class) results in new style class which is not allowed as exception (mixing old and new style classes is probably bad anyway).

Use collections.UserDict to avoid metaclass conflicts:

class ConstraintFailureSet(coll.UserDict, Exception):
        """
            Container for constraint failures. It act as a constraint failure itself
            but can contain other constraint failures that can be accessed with a dict syntax.
        """

        def __init__(self, **failures) :
            coll.UserDict.__init__(self, failures)
            Exception.__init__(self)


print( isinstance(ConstraintFailureSet(), Exception)) #True
raise ConstraintFailureSet()
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top