0

There is a similar question here, but if refers to "reclassing", while i want a subclass.

Suppose, i have a Django/SQLAlchemy exception IntegrityError, and i want to have a subclass of it called AlreadyExists for a special case when a unique constraint was violated:

    try:
         ...
    except IntegrityError as exc:
        if ('violates unique constraint '
                '"customer_wish_customer_product_unique"') in exc.message:
            raise AlreadyExists(exc)

I don't want to do just a subclass, because IntegrityError.__init__ has many arguments, which i don't want to, manually extract from existing IntegrityError instance and feed them to the parent __init__.

So i came up with the following code:

class AlreadyExists(IntegrityError):

    def __new__(cls, sa_exc):
        sa_exc.__class__ = cls  # replace the class
        return sa_exc  # but do not create new object

    def __init__(self, *args):
        pass  # do nothing, as no new object was created

What do you think about the idea itself and can you suggest a better implementation?

Community
  • 1
  • 1
warvariuc
  • 57,116
  • 41
  • 173
  • 227
  • Is this really any different than `exc.__class__ = AlreadyExists; raise exc`? – mgilson Jul 24 '13 at 06:38
  • There are two differences: 1) Duplicate code each time you want to raise the exception. 2) This way `isinstance(exc, IntegrityError) == False` – warvariuc Jul 24 '13 at 06:54
  • Are you sure about (2)? Your new class is still a subclass. – Fredrik Jul 24 '13 at 07:01
  • Yes, you are right about (2) - `AlreadyExists` is a subclass of `IntegrityError` – warvariuc Jul 24 '13 at 07:18
  • Regarding 2 -- My simple tests indicate that isinstance looks at the `__class__` attribute (and then probably walks up the classes in the `__mro__` or the `__bases__` attribute). In any event, my proposal would pass the `isinstance` check. -- Regarding 1, I don't see how explicitly setting the class is any more duplicate code than calling a confusing class "constructor". (your `__new__` which really mutates the input class as opposed to constructing a new instance of the class you have). – mgilson Jul 24 '13 at 07:18
  • But in the `__new__` method i can put as many comments as i need to explain what is happening, vs. doing `exc.__class__ = AlreadyExists` everywhere and hoping that people will understand – warvariuc Jul 24 '13 at 07:37
  • You could just add a function that promotes the exception by assigning to `__class__`. I think that's clearer than the `__new__/__init__` dance, and avoids duplications. And with a good enough function name, it can be fairly self-documenting at the call sites. – Fredrik Jul 24 '13 at 07:39
  • Or better make a `classmethod` like `AlreadyExists.from_sa_exc(sa_exc)` ? – warvariuc Jul 24 '13 at 07:43
  • maybe smth like this: `def __new__(some...): raise Error #you cannot create object` and after in the code write another class which will be inhereted from `AlreadyExists` if you need to create object – Dmitry Zagorulkin Jul 24 '13 at 09:20

0 Answers0