An interesting exception handling quirk
I've been looking at Terry's suggestion of using a class based implementation for contextlib.suppress, and rediscovered why I suggested Raymond switch the original implementation to using @contextmanager: recreating the *exact* exception subclass check from Python is actually difficult these days. The exception handling only looks for concrete subclasses, ignoring the dynamic subclass hook:
import abc class ExceptionABC(abc.ABC, Exception): ... pass ... ExceptionABC.register(ZeroDivisionError)
issubclass(ZeroDivisionError, ExceptionABC) True try: ... 1/0 ... except ExceptionABC: ... print("It's a subclass of ExceptionABC!") ... except ZeroDivisionError: ... print("But not according to the exception handling!") ... But not according to the exception handling!
I don't actually have a problem with the current behaviour (since raise and except both enforce a requirement for the types involved to be concrete subclasses of BaseException), but as far as I'm aware we don't explicitly state anywhere that if exception handling and an issubclass check give different answers for whether or not exception A is a subclass of exception B, then the fault for any resulting differences in behavious lies with the way A and/or B are defined, not with the code using issubclass to check for exception inheritance. Having noticed the discrepancy, I feel like it should be explicitly recorded somewhere in the language reference, I'm just not sure where. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia
On Sat, 19 Oct 2013 21:41:17 +1000
Nick Coghlan
Having noticed the discrepancy, I feel like it should be explicitly recorded somewhere in the language reference, I'm just not sure where.
Since it's a quirk, I don't think it should be mentioned in the language reference. Also, see http://bugs.python.org/issue12029 Regards Antoine.
On 19 October 2013 22:53, Antoine Pitrou
On Sat, 19 Oct 2013 21:41:17 +1000 Nick Coghlan
wrote: Having noticed the discrepancy, I feel like it should be explicitly recorded somewhere in the language reference, I'm just not sure where.
Since it's a quirk, I don't think it should be mentioned in the language reference. Also, see http://bugs.python.org/issue12029
Ah, thanks for the reference. I'll comment further over there :) Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia
Hi Nick,
On Sat, Oct 19, 2013 at 1:41 PM, Nick Coghlan
recreating the *exact* exception subclass check from Python is actually difficult these days.
Can't it be done roughly like that? def __exit__(self, typ, val, tb): try: raise typ, val except self.exceptions: return True except: return False A bientôt, Armin.
On 20 October 2013 16:08, Armin Rigo
Hi Nick,
On Sat, Oct 19, 2013 at 1:41 PM, Nick Coghlan
wrote: recreating the *exact* exception subclass check from Python is actually difficult these days.
Can't it be done roughly like that?
def __exit__(self, typ, val, tb): try: raise typ, val except self.exceptions: return True except: return False
In Python 3, you have to use "raise type if val is None else val" instead, and you then have to deal with the fact that raise will overwrite an already set __context__ on the exception value (contextlib.ExitStack actually has to work around that when unwinding the stack by restoring a more appropriate __context__ value). But yes, actually reraising it does let you reproduce the exception matching. That said, it actually occurs to me now that the current behaviour (overwriting an already set __context__) could arguably be considered a bug, since we don't overwrite an already set __traceback__. Cheers, Nick. -- Nick Coghlan | ncoghlan@gmail.com | Brisbane, Australia
participants (3)
-
Antoine Pitrou
-
Armin Rigo
-
Nick Coghlan