On Wed, Mar 04, 2020 at 12:19:25PM +0200, Alex Hall wrote:
Can you explain where `except ParameterError` would produce better code than the best current alternative?
Serhiy came up with a good use-case that I hadn't considered: functions which take arbitrary callback functions as argument, e.g. sorted() with a key function. Both of these calls raise TypeError, but for different reasons: # right number of arguments passed to the key function, # but the wrong argument type sorted([3, 5, 1], key=len) # wrong number of arguments passed to the key function sorted([3, 5, 1], key=isinstance) It might be useful to be able to distinguish the two cases. My earlier use-case still stands: feature detection where a function has changed its parameter list. More on this below.
It seems to me that using this is like stating "this code might be wrong" and would generally produce bad code.
It seems to me that your comment above could be equally said about *any* use of exceptions: "this code might be wrong, so we'll stick it in a try...except block". Only that's not how most of us actually use exceptions.
For example if I wanted to use math.gcd on multiple values but I need to support Python versions that only allow two arguments, then I'll just apply it to two arguments at a time, maybe with functools.reduce.
I've done that myself. Not specifically with gcd, but with other functions, which I don't remember off the top of my head. gcd is a particularly simple example because it is so easy to wrap. Excluding function header and docstring, the code is just six lines: if len(args) == 0: return 1 elif len(args) == 1: return abs(args[0]) else: return reduce(_gcd, args) Not all cases will be that simple, or they may involve a serious performance cost. In general, the official version is going to be faster and better tested than the wrapper version. Why shouldn't I use the fast, well-tested standard version if it is available? I just need a reliable way to detect the needed feature. This is not specifically about gcd. It could be about functions of arbitrary complexity: try: # Test whether the aardvark function supports a # `hovercraft` parameter. result = aardvark( spam, eggs, cheese, hovercraft=1 ) except ParameterError: # No hovercraft parameter allowed, create a wrapper. ... TypeError is less satisfactory because I cannot easily distinguish the two cases: 1. The 'hovercraft' parameter is not available; 2. The 'hovercraft' parameter is available, but I have made some other error in the function call which results in a TypeError.
And in general if I want to distinguish between versions I'd much rather just check the version and thus assert "this code is correct for this version".
Feature detection is more reliable. What if the caller of my library has back-ported the new, advanced version of aardvark() to their Python? Instead of using the faster, better tested official backport, my code will use my wrapper. What if the caller is using my library with another interpreter, say PyPy or Jython, which doesn't support the feature I need? With feature detection, my code will simply fall back to the wrapper. With version checking, it will wrongly assert the feature is available, and then crash later. As far as I can tell, feature detection has more or less completely overshadowed version checks in the browser and Javascript space. In my opinion, checking the version should only be used when it is too hard to detect the feature.
If you catch ParameterError, how do you know that it came directly from the line you caught it from and not deeper within?
Does it matter? If the call aardvark(*args, hovercraft=1) leaks a ParameterError from deep inside its internals, then even if the feature is technically available according to a version check, it is too buggy to use and I need to use my wrapper.
If you want to check that you're passing parameters properly, I suggest writing code like this:
import inspect
signature = inspect.signature(func)
And what if the function I'm checking doesn't support signature metadata? https://docs.python.org/3/library/inspect.html#inspect.signature I can wrap the call to signature() in try...except, but what's my fallback if I can't check the signature? py> inspect.signature(math.gcd) Traceback (most recent call last): [...] TypeError: 'feature_version' is an invalid keyword argument for compile() -- Steven