2

I'm using a third party API which emits an HttpError.

By catching this error I can inspect the http response status and narrow down the problem. So now I would like to emit a more specific HttpError which I'll dub BackendError and RatelimitError. The latter has context variables to be added.

How do I create a custom exception which inherits from HttpError and can be created without losing the original exception?

The question is actually polymorphism 101 but my head is fuzzy today:

class BackendError(HttpError):
    """The Google API is having it's own issues"""
    def __init__(self, ex):
        # super doesn't seem right because I already have
        # the exception. Surely I don't need to extract the
        # relevant bits from ex and call __init__ again?!
        # self = ex   # doesn't feel right either


try:
     stuff()
except HttpError as ex:
     if ex.resp.status == 500:
         raise BackendError(ex)

How do we catch the original HttpError and encapsulate it so it is still recognisable as both an HttpError and a BackendError?

thefourtheye
  • 233,700
  • 52
  • 457
  • 497
John Mee
  • 50,179
  • 34
  • 152
  • 186
  • Oops, Sorry. I was focusing more on the incomplete `BackendError`. – thefourtheye Apr 28 '15 at 02:27
  • Which Python version? 2.7 and 3.x have special forms of the `raise` statement that can change the exception class while preserving the original traceback object. On earlier versions I think you're stuck either mutating the exception on the way out (if you, say, just want to edit the attributes) or raising a new one built from the old but losing the full traceback in the process. – Peter DeGlopper Apr 28 '15 at 02:33
  • Yeah, sadly it is on python 2.7 – John Mee Apr 28 '15 at 02:34
  • @PeterDeGlopper Can you point us to more about that? – John Mee Apr 28 '15 at 02:45
  • @PeterDeGlopper You must mean this python 3 `raise thiserror from thaterror` [feature here](https://docs.python.org/3/library/exceptions.html)? I haven't found it for py2 yet. Looking... – John Mee Apr 28 '15 at 02:49
  • @JohnMee Does [this](http://stackoverflow.com/a/16414892/1903116) answer your question? – thefourtheye Apr 28 '15 at 02:55
  • @thefourtheye Indeed it does. Thx for your help! – John Mee Apr 28 '15 at 02:59
  • 1
    I was thinking of the three-argument form of 2.7's `raise`: https://docs.python.org/2/reference/simple_stmts.html#raise - but if 3.x style exception chaining is a thing you can do it's clearly better. – Peter DeGlopper Apr 28 '15 at 03:20

1 Answers1

2

If you look at the actual definition of googleapiclient.errors.HttpError,

__init__(self, resp, content, uri=None) 

So, after inheriting you need to initialize the base class with all those values.

class BackendError(HttpError):
    """The Google API is having it's own issues"""
    def __init__(self, resp, content, uri=None):
        # Invoke the super class's __init__
        super(BackendError, self).__init__(resp, content, uri)

        # Customization can be done here

And then when you catch the Error,

except HttpError as ex:
     if ex.resp.status == 500:
         raise BackendError(ex.resp, ex.content, ex.uri)

If you don't want the client to explicitly unpack the contents, you can accept the HTTPError object in the BackendError's __init__ and then you can do the unpacking, like this

class BackendError(HttpError):
    """The Google API is having it's own issues"""
    def __init__(self, ex):
        # Invoke the super class's __init__
        super(BackendError, self).__init__(ex.resp, ex.content, ex.uri)

        # Customization can be done here

and then you can simply do

except HttpError as ex:
     if ex.resp.status == 500:
         raise BackendError(ex)
thefourtheye
  • 233,700
  • 52
  • 457
  • 497