uplink: How should exceptions from underlying client libraries be handled?
Suppose I have a consumer for some API and suppose there is some network or server problems resulting in some sort of connection error. Currently ConnectionError from requests or ClientConnectionError from aiohttp are raised and it results in that calling code must know which library is used and it must handle different exceptions in synchronous and asynchronous case, this is rather inconvenient. Situation becomes worse if I make some another adapter, for example with pycurl: interface and calling convention remain the same but very different exceptions are raised.
I can see 2 ways to deal with it:
- define such behaviour to be by design and probably reexport exception classes as attributes of consumer instance or do nothing for simplicity and put the burden on user’s code
- define some
uplink’s exceptions and wrap libraries’ errors in them However both of these ways may lead to information loss which may be not desirable or may be not.
What are your thoughts about it?
And one more question: suppose I want to handle some exceptions or errors in Consumer to do something with them, for example to wrap them in my custom exception, how can I do it?
About this issue
- Original URL
- State: closed
- Created 6 years ago
- Reactions: 2
- Comments: 15 (15 by maintainers)
Thank you, I really appreciate your effort to solve this issue. I like that it is responsibility of an adapter to correctly expose required exceptions, also I like that http client now carries all essential bits of interface to use.
@daa - I took another shot at this: #117
Dynamic superclassing is clever but I’m afraid it’s too hacky and may rely on implementation details, and personally I prefer more simple and straightforward code. Also Python data model documentation (https://docs.python.org/3/library/stdtypes.html#special-attributes) tells us that
__bases__is supposed to be read-only attribute even if it can be changed actually but not for built-in exceptions. I’ll repeat this is clever trick but it patches unrelated code someone other is responsible for, which may be seen as a problem from maintenance point of view, and articles and stackoverflow answers describing similar technique warn that this should not be normally used.At all I think problems and hacks arise from two contradictory wishes:
At the end I have another idea, may be not so bright as dynamically setting superclass, and involving more code - you may define hierarchy of client exceptions and then dynamically construct proxy classes for real exceptions as inherited from real exception class and corresponding
uplinkclient exception class and then reraise them instead of original. It’s not very elegant but sufficiently self-contained.Also you may look at django if you haven’t already - they had similar issue and similar thoughts - https://code.djangoproject.com/ticket/15901 - and decided to wrap exceptions to their own, but they had simpler case - all needed exceptions were listed in pep and libraries conformed to it.
Again, thank you for your work, this issue happened to be rather difficult and non-obvious and involve different tradeoffs.
Thank you, nice work. It’s a pity that approach with virtual subclasses didn’t work, it was a nice idea. Exceptions proxying should work for most cases. But I can see one drawback following from that proxies are tuples named like exceptions but not exceptions themselves: one cannot catch several client exceptions in usual manner. For example following code will not work at python3:
It will result in
TypeError: catching classes that do not inherit from BaseException is not allowed, one should writeexcept client_exceptions.ConnectionError + client_exceptions.InvalidUrl:instead. Interestingly that code will work for python2, andisinstance(e, (client_exceptions.ConnectionError, client_exceptions.InvalidUrl))at both Pythons will not complain and work as supposed. However, I don’t know how to do better than you’ve done. Also it’s questionable whether such exception handling as in example is reasonable, sane and required at all.Started working on this in #70.
FYI: @daa