0

So I'm (of course) writing a library that interacts with an API, and the API can sometimes return errors. I came up with a context manager to catch dynamic errors, but I'd like to do something a little quicker:

class APIError(Exception):
    def __getattr__(self, name):
        setattr(self, name, type(name, (APIError,), {}))
        return getattr(self, name)

What I'd then like to do is something like this:

try:
    ...
except APIError.knownerrorcode:
    ...handle...

However, this doesn't work - the __getattr__ doesn't apply to the class itself. The workaround is to do this:

try:
    ...
except APIError().knownerrorcode:
    ...handle...

but this is ugly and normally wouldn't make sense - you can't catch exception instances, only classes, but APIError() obviously creates an instance.

To solve that problem, I added APIError = APIError() after the class declaration, and then the second example worked fine. However, after that change, this failed:

try:
    ...
except APIError:
    ...handle...

because APIError is now an instance and cannot be directly caught. The following doesn't work properly either:

class APIError(Exception):
    @classmethod
    def __getattr__(cls, name):
        setattr(cls, name, type(name, (cls,), {}))
        return getattr(cls, name)

because the decorator converts it into a normal method:

>>> APIError.knownerrorcode
Traceback (most recent call last):
  File "<pyshell#75>", line 1, in <module>
    APIError.knownerrorcode
AttributeError: type object 'APIError' has no attribute 'knownerrorcode'
>>> APIError.__getattr__('knownerrorcode')
<class '__main__.knownerrorcode'>

I've also tried metaclasses:

class meta(type):
    def __getattr__(self, name):
        setattr(self, name, type(name, (APIError,), {}))
        return getattr(self, name)

class APIError(Exception, meta):
    pass

but the second declaration results in a TypeError:

Traceback (most recent call last):
  File "<pyshell#66>", line 1, in <module>
    class APIError(Exception, meta):
TypeError: multiple bases have instance lay-out conflict

So, my question is: How can I define a __getattr__ on a class, so that the class can be caught as an exception and all attributes of the class return subclasses of the root?

AbyxDev
  • 1,363
  • 16
  • 30
  • @Aran-Fey (have to guess at the ping) https://meta.stackoverflow.com/questions/373396/why-was-my-question-marked-as-a-duplicate – Yunnosch Aug 30 '18 at 06:41
  • @Yunnosch Nope, thanks for telling me about it. – Aran-Fey Aug 30 '18 at 06:45
  • 2
    You are using the metaclass incorrectly. You're using it as a base class rather than a metaclass. Change `class APIError(Exception, meta):` to `class APIError(Exception, metaclass=meta):` and it'll work. (Assuming you're using python 3.) The correct way to use metaclasses is also shown in the duplicate. – Aran-Fey Aug 30 '18 at 07:15

0 Answers0