Soni L. writes:
the fact that exception handlers aren't reusable and composable is really bothering me so I would like to propose something to make them reusable and composable.
for example, if I have this mess:
Looks nice and flat and orderly to me; the semantics are messy, so I don't see how you can hide them. The method as written makes sense to me. It specializes in keeping all the exception handling in one place, while the "real work" is done elsewhere (factory(), self._obj, .get_supported_properties()). You can only disperse those semantics into functions, making it harder to read. "Hard to read" is especially true when you use a real-world example, full of identifiers with unknown semantics. It probably makes more sense in context of the application, and again in that context it might be a lot more persuasive that you want "reusable handlers" vs. a function that specializes in "collecting handling".
def get_property_values(self, prop): try: factory = self.get_supported_properties()[prop] except KeyError as exc: raise PropertyError from exc iterator = factory(self._obj) try: first = next(iterator) except StopIteration: return (x for x in ()) except abdl.exceptions.ValidationError as exc: raise LookupError from exc return itertools.chain([first], iterator)
I get to refactor it into:
def keyerror_handler(exc): raise PropertyError from exc
def other_handler(exc): if isinstance(exc, StopIteration): return (x for x in ()) raise LookupError from exc
def get_property_values(self, prop): try: factory = self.get_supported_properties()[prop] except KeyError with keyerror_handler: pass iterator = factory(self._obj) try: first = next(iterator) except (StopIteration, abdl.exceptions.ValidationError) with other_handler as result: return result return itertools.chain([first], iterator)
I don't understand the semantics of "with". Do you mean def get_property_values(self, prop): try: factory = self.get_supported_properties()[prop] except KeyError as exc: keyerror_handler(exc) iterator = factory(self._obj) try: first = next(iterator) except (StopIteration, abdl.exceptions.ValidationError) as result: other_handler(result) return result return itertools.chain([first], iterator) If so, who needs "with"?
another idea would be to use semicolons:
There's no need for semicolons (or keyword abuse, for that matter). Also, they may not be grammatically ambiguous here, but Python already has semicolons for dividing a physical line into logical lines, whereas here it's an abbreviation for "apply the preceding function to the current exception and return the result from the enclosing function," which is pretty powerful semantics for five pixels on my screen.