On 24 June 2017 at 22:31, Greg Ewing email@example.com wrote:
Steven D'Aprano wrote:
I think we're over-generalizing this problem. There's two actual issues here, and we shouldn't conflate them as the same problem:
(1) People write buggy code based on invalid assumptions of what can and can't raise. E.g.:
(2) There's a *specific* problem with property where a bug in your getter or setter that raises AttributeError will be masked, appearing as if the property itself doesn't exist.
Case 1 can usually be handled by rewriting the code so as to make the scope of exception catching as narrow as possible.
Case 2 needs to be addressed within the method concerned on a case-by-case basis. If there's a general principle there, it's something like this: If you're writing a method that uses an exception as part of it's protocol, you should catch any incidental occurrences of the same exception and reraise it as a different exception.
I don't think there's anything more the Python language could do to help with either of those.
While I used to think that, I'm no longer sure it's true, as it seems to me that a `contextlib.convert_exception` context manager could help with both of them. (That's technically the standard library helping, rather than the language per se, but it's still taking a currently obscure implementation pattern and making it more obvious by giving it a name)
So if we assume that existed, and converted the given exception to RuntimeError (the same way PEP 479 does for StopIteration), we'd be able to write magic methods and properties in the following style:
def __getitem__(self, n): self.validate(n) with contextlib.convert_exception(IndexError): # If we get an IndexError in here, it's a bug return self._getitem(n)
That idiom then works the same way regardless of how far away your code is from the exception handler you're trying to bypass - you could just as easily put it inside the `self._getitem(n)` helper method instead.