views:

736

answers:

5

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...

+3  A: 

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.

Dave Costa
Added version : 2.4 :-)
e-satis
+2  A: 

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 ;-)

e-satis
+6  A: 

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
hop
+4  A: 

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'>
S.Lott
Your solution imply I have to rewrite the whole dictionary interface using magic method (setitem, items, iteritems, len, etc) meaning adding manually a dozen of methods that I'll have to unit test too.
e-satis
Are you actually using ALL dictionary features? Why can't use e.failures to explicitly reference the attached dictionary?
S.Lott
I will need at least the features to list / iterate on keys, values, the len and get.setitem. But don't bohter, UserDict will do it since I don't think this code is a bottleneck... Thanks for your proposal anyway :-)
e-satis
So, you need five methods: keys, values, items, and __setitem__? I'm unclear on what you're looking to do and what the "dozens" might encompass. Do you have some other methods in mind or some other features?
S.Lott
A: 

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).

lispmachine
If what you say is right, we should be able to inherit from Dict and Exception in V3 ?
e-satis
As mentioned by Dave Costa in Python 2.5 Exception and dict can not be in same inheritance hierarchy because of them being implemented in C and stored differently. This also applies for any built-in (new style class) type even in previous versions. Python 2.6 and 3 do not seem to change anything on this topic http://docs.python.org/3.0/whatsnew/3.0.html
lispmachine