On Sat, Apr 11, 2020 at 4:35 AM Soni L. email@example.com wrote:
in one of my libraries (yes this is real code. all of this is taken from stuff I'm deploying.) I have the following piece of code:
def _extract(self, obj): try: yield (self.key, obj[self.key]) except (TypeError, IndexError, KeyError): if not self.skippable: raise exceptions.ValidationError
this library handles all sorts of arbitrary objects - dicts, sets, lists, defaultdicts, wrappers that are registered with collections.abc.Sequence/Mapping/Set, self-referential data structures, and whatnot. (and btw can we get the ability to index into a set to get the originally inserted element yet) - which means I need to treat all sorts of potential errors as errors. however, sometimes those aren't errors, but intended flow control, such as when your program's config has an integer list in the "username" field. in that case, I raise a ValidationError, and you handle it, and we're all good. (or sometimes you want to skip that entry altogether but anyway.)
due to the wide range of supported objects, I can't expect the TypeError to always come from my attempt to index into a set, or the IndexError to always come from my attempt to index into a sequence, or the KeyError to always come from my attempt to index into a mapping. those could very well be coming from a bug in someone's weird sequence/mapping/set implementation. I have no way of knowing!
If someone's __getitem__ raises LookupError (which covers both Index and Key), you should assume that the key wasn't found. That is exactly what the protocol is. If that exception was the result of a bug, that is no different from it unexpectedly returning None when it should have returned a value - it's a bug, and it's not up to you to try to handle it.
TypeError is a bit less obvious, but if this is really an issue, just special-case sets, and have done with it.
Don't try to protect against everyone else's theoretically-possible bugs. You can never catch them all anyway. At some point, you just have to let the blame land where it belongs, and let your code fail if the code it's calling on is buggy. What you're suggesting here is on par with saying "but what if someone uploads a newer version of a package to PyPI and gives it a lower version number?". It's not pip's problem to fix that, and it's not pip's fault if it gets things wrong.