18

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

Bite code
  • 578,959
  • 113
  • 301
  • 329

6 Answers6

21

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
4

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
  • 384,516
  • 81
  • 508
  • 779
  • 1
    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. – Bite code Nov 22 '08 at 09:25
  • 1
    Are you actually using ALL dictionary features? Why can't use e.failures to explicitly reference the attached dictionary? – S.Lott Nov 22 '08 at 13:35
  • 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 :-) – Bite code Nov 24 '08 at 08:48
  • 1
    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 Nov 24 '08 at 11:39
3

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.

vaultah
  • 44,105
  • 12
  • 114
  • 143
Dave Costa
  • 47,262
  • 8
  • 56
  • 72
3

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

Bite code
  • 578,959
  • 113
  • 301
  • 329
  • GvR warned about this in his "Unifying types and classes in Python 2.2", btw (see, http://www.python.org/download/releases/2.2.2/descrintro/, `"You can use multiple inheritance, but you can't multiply inherit from different built-in types (for example, you can't create a type that inherits from both the built-in dict and list types)."`) – mlvljr Jun 08 '11 at 20:54
0

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()
codeMonkey
  • 562
  • 8
  • 19
0

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
  • 4,407
  • 1
  • 22
  • 31
  • If what you say is right, we should be able to inherit from Dict and Exception in V3 ? – Bite code May 29 '09 at 11:54
  • 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 May 29 '09 at 13:10