Hi, Here's something I wanted in Python for many years. If this has been discussed in the past, please refer me to that discussion. On one hand, it's something that I can't imagine the python-dev community supporting. On the other hand, it would maintain backward compatibility. I wish there were a 100 more built-in exceptions in Python, that will be very specific about what went wrong. If I do this: >>> x, y = range(3) I know it'll raise a ValueError, because I've memorized that, but it did take me a few years to remember where I should expect ValueError and where I should expect TypeError. It would be nice if the operation above raised UnpackingOverflowError, which will be a subclass of UnpackingError, along with UnpackingUnderflowError. UnpackingError can be a subclass of ValueError, for backward compatibility. Similarly, if I did this: >>> def f(x, y): return x + y >>> f(1) I would get a TypeError. Would be a lot cooler if I got MissingArgumentsError, which would be a subclass of SignatureError, which would be a subclass of TypeError. There are 2 reasons I want this: 1. When I'm writing a try..except clause, I want to catch a specific exception like MissingArgumentsError rather than ValueError or TypeError. They're too ubiquitous. I don't want some other unexpected failure producing the same ValueError and triggering my except clause. 2. When I get an error, especially from some shitty corporate system that truncates the traceback, I want to get as many hints as possible about what went wrong. It's true that today, most Python exceptions have good text in their message, like "TypeError: f() missing 1 required positional argument: 'y'". But that isn't guaranteed everywhere, and specific exception types could help. What do you think? Ram.
01.05.20 09:48, Ram Rachum пише:
I wish there were a 100 more built-in exceptions in Python, that will be very specific about what went wrong.
If I do this:
>>> x, y = range(3)
I know it'll raise a ValueError, because I've memorized that, but it did take me a few years to remember where I should expect ValueError and where I should expect TypeError.
It would be nice if the operation above raised UnpackingOverflowError, which will be a subclass of UnpackingError, along with UnpackingUnderflowError. UnpackingError can be a subclass of ValueError, for backward compatibility.
Could you please provide a list of these 100 exceptions? If you create a PR, with documentation and tests, it would be a good start of the discussion.
On Fri, May 1, 2020 at 10:28 AM Serhiy Storchaka <storchaka@gmail.com> wrote:
Could you please provide a list of these 100 exceptions? If you create a PR, with documentation and tests, it would be a good start of the discussion.
That's not a reasonable request. If there's enough support for this idea, then I'll be happy to start working on this, but even then it'll be a collaborative effort.
01.05.20 10:32, Ram Rachum пише:
On Fri, May 1, 2020 at 10:28 AM Serhiy Storchaka <storchaka@gmail.com <mailto:storchaka@gmail.com>> wrote:
Could you please provide a list of these 100 exceptions? If you create a PR, with documentation and tests, it would be a good start of the discussion.
That's not a reasonable request. If there's enough support for this idea, then I'll be happy to start working on this, but even then it'll be a collaborative effort.
There is nothing to discuss without the subject of discussion.
On Fri, May 1, 2020 at 4:34 AM Ram Rachum <ram@rachum.com> wrote:
On Fri, May 1, 2020 at 10:28 AM Serhiy Storchaka <storchaka@gmail.com> wrote:
Could you please provide a list of these 100 exceptions? If you create a PR, with documentation and tests, it would be a good start of the discussion.
That's not a reasonable request.
If there's enough support for this idea, then I'll be happy to start
working on this, but even then it'll be a collaborative effort.
I believe the first step would be to show that which combination of current_exception + messages should be split into various new exceptions. To give a possible concrete example, if you have a look at https://aroberge.github.io/friendly-traceback-docs/docs/html/tracebacks_en_3..., you will see that TypeError can have various messages such as: * must be X not Y (where X might be str and Y might be list, etc.) * can only concatenate X (not Y) to X * unsupported operand type(s) X for: Y and Z * X not supported between instances of Y and Z * bad operand type for unary X: Y * X object does not support item assignment * X takes Y positional arguments but Z was given * X missing 2 required positional arguments: Y and Z * X object is not callable There are likely many more cases only for TypeError (not to mention other types of errors.) Note that there are actual tests for each of these (which is one important criterion as mentioned by Serhly). (This comment does not mean that I am not necessarily expressing support for adding many new exception types.) You're welcome to start collaborating and help document all possible cases. This invitation to collaborate is, of course open to all. André Roberge
_______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/IVNKPA... Code of Conduct: http://python.org/psf/codeofconduct/
I thought of a third advantage to this approach: Except clauses will be easier to understand. If I'm reading someone's code and I see `except ValueError`, I'm gonna have to do a bit of thinking to figure out what exactly they're catching. On the other hand, if the code is `except IntParsingError`, then I know exactly what it is they're catching. On Fri, May 1, 2020 at 9:48 AM Ram Rachum <ram@rachum.com> wrote:
Hi,
Here's something I wanted in Python for many years. If this has been discussed in the past, please refer me to that discussion.
On one hand, it's something that I can't imagine the python-dev community supporting. On the other hand, it would maintain backward compatibility.
I wish there were a 100 more built-in exceptions in Python, that will be very specific about what went wrong.
If I do this:
>>> x, y = range(3)
I know it'll raise a ValueError, because I've memorized that, but it did take me a few years to remember where I should expect ValueError and where I should expect TypeError.
It would be nice if the operation above raised UnpackingOverflowError, which will be a subclass of UnpackingError, along with UnpackingUnderflowError. UnpackingError can be a subclass of ValueError, for backward compatibility.
Similarly, if I did this:
>>> def f(x, y): return x + y >>> f(1)
I would get a TypeError. Would be a lot cooler if I got MissingArgumentsError, which would be a subclass of SignatureError, which would be a subclass of TypeError.
There are 2 reasons I want this:
1. When I'm writing a try..except clause, I want to catch a specific exception like MissingArgumentsError rather than ValueError or TypeError. They're too ubiquitous. I don't want some other unexpected failure producing the same ValueError and triggering my except clause.
2. When I get an error, especially from some shitty corporate system that truncates the traceback, I want to get as many hints as possible about what went wrong.
It's true that today, most Python exceptions have good text in their message, like "TypeError: f() missing 1 required positional argument: 'y'". But that isn't guaranteed everywhere, and specific exception types could help.
What do you think?
Ram.
On Fri, May 01, 2020 at 11:36:40AM +0300, Ram Rachum wrote:
I thought of a third advantage to this approach: Except clauses will be easier to understand.
If I'm reading someone's code and I see `except ValueError`, I'm gonna have to do a bit of thinking to figure out what exactly they're catching.
You say that as if that's a bad thing. Except in the most blindingly obvious and simple cases, you're going to have to do that anyway. Working out what is being caught is not the hard part, unless you abuse try...except clauses: try: main() except: print("an error occurred") is a classic example of exception misuse. But there are many smaller ways to abuse them too. The hard part is usually figuring out *why* the exception occurred, not *what* you are catching.
On the other hand, if the code is `except IntParsingError`, then I know exactly what it is they're catching.
Will you? Since I don't know what functions might raise IntParsingError, I can't tell what it might have raised it. I mean, sure, it is obviously something parsing an int in some sense, but that's pretty wide: - must it be parsing a string? could it be bytes? - parsing a fraction? - might it be a call to `eval` or `compile` or only `int`? I think we need to distinguish between the two use-cases for exceptions: - to halt processing and report an error to the developer; - to be caught and recovered from. In this case, I don't think there is any benefit to me, the developer, to read: IntParsingError: invalid literal for int() with base 10: 'a' over the status quo: ValueError: invalid literal for int() with base 10: 'a' and from the perspective of debugging, it's just bloat: - one more thing to document - one more thing for the test suite to test - one more thing to learn - one more thing to misspell (is it IntParsingError or IntParseError or IntParsingException?) - one more class using up memory - one more thing for IDEs and syntax colourisers to check for etc. From the perspective of catching and recovering from errors, again, I doubt that there is much benefit to well-written code: try: num = int(astring) except ValueError: recover() is fine, changing the ValueError to IntParsingError gains nothing. The only advantage I can see is if your try block falls into that very narrow zone where it is large enough to contain *exactly* one attempt to parse an int *but also* at least one other function call which might raise an unrelated ValueError: try: a, b = int(astring), float(alist) except IntParsingError: ... But I'm not sure that this is valuable enough to make up for the bloat. -- Steven
Hi Ram, I think you are confusing the exception type with the exception reason. By adding 100 more exception types, you don't make things easier, but instead you complicate things, since we'd all have to memorize those 100 exception types. That said, enhancing the error reasons texts is certainly something that would help everyone. Adding more exception types to the stack makes sense when there's a dedicated need to catch only specific sub types, but even there it's already possible to add this extra information to the exception objects as e.g. .errno or similar attribute. Thanks, -- Marc-Andre Lemburg eGenix.com Professional Python Services directly from the Experts (#1, May 01 2020)
Python Projects, Coaching and Support ... https://www.egenix.com/ Python Product Development ... https://consulting.egenix.com/
::: We implement business ideas - efficiently in both time and costs ::: eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611 https://www.egenix.com/company/contact/ https://www.malemburg.com/ On 01.05.2020 08:48, Ram Rachum wrote:
Hi,
Here's something I wanted in Python for many years. If this has been discussed in the past, please refer me to that discussion.
On one hand, it's something that I can't imagine the python-dev community supporting. On the other hand, it would maintain backward compatibility.
I wish there were a 100 more built-in exceptions in Python, that will be very specific about what went wrong.
If I do this:
>>> x, y = range(3)
I know it'll raise a ValueError, because I've memorized that, but it did take me a few years to remember where I should expect ValueError and where I should expect TypeError.
It would be nice if the operation above raised UnpackingOverflowError, which will be a subclass of UnpackingError, along with UnpackingUnderflowError. UnpackingError can be a subclass of ValueError, for backward compatibility.
Similarly, if I did this:
>>> def f(x, y): return x + y >>> f(1)
I would get a TypeError. Would be a lot cooler if I got MissingArgumentsError, which would be a subclass of SignatureError, which would be a subclass of TypeError.
There are 2 reasons I want this:
1. When I'm writing a try..except clause, I want to catch a specific exception like MissingArgumentsError rather than ValueError or TypeError. They're too ubiquitous. I don't want some other unexpected failure producing the same ValueError and triggering my except clause.
2. When I get an error, especially from some shitty corporate system that truncates the traceback, I want to get as many hints as possible about what went wrong.
It's true that today, most Python exceptions have good text in their message, like "TypeError: f() missing 1 required positional argument: 'y'". But that isn't guaranteed everywhere, and specific exception types could help.
What do you think?
Ram.
_______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/XZXWXI... Code of Conduct: http://python.org/psf/codeofconduct/
On Fri, May 1, 2020 at 11:37 AM M.-A. Lemburg <mal@egenix.com> wrote:
Hi Ram,
I think you are confusing the exception type with the exception reason.
Some time ago `ModuleNotFoundError` was added as a subclass of `ImportError`, which I really liked. Was this also another instance of a confusion between the exception type and the exception reason?
By adding 100 more exception types, you don't make things easier, but instead you complicate things, since we'd all have to memorize those 100 exception types.
Why would you have to memorize them? If you're writing an except clause, you could still write `except ValueError:` and it'll work the same as before. Do you feel that the addition of `ModuleNotFoundError` meant that you now had to memorize a new exception?
On 01.05.2020 10:44, Ram Rachum wrote:
On Fri, May 1, 2020 at 11:37 AM M.-A. Lemburg <mal@egenix.com <mailto:mal@egenix.com>> wrote:
Hi Ram,
I think you are confusing the exception type with the exception reason.
Some time ago `ModuleNotFoundError` was added as a subclass of `ImportError`, which I really liked. Was this also another instance of a confusion between the exception type and the exception reason?
No, because the ImportError can actually mean a lot of things (the import machinery being rather complex) and you will want to be able to catch the error situation where the module is not installed, rather than e.g. an error which happened while loading an installed module.
By adding 100 more exception types, you don't make things easier, but instead you complicate things, since we'd all have to memorize those 100 exception types.
Why would you have to memorize them? If you're writing an except clause, you could still write `except ValueError:` and it'll work the same as before.
Do you feel that the addition of `ModuleNotFoundError` meant that you now had to memorize a new exception?
Sure, because I would not know that this is actually a subclass of ImportError. The hierarchy already has quite a few exeption types and it's not always clear that one type is subclass of another: https://docs.python.org/3/library/exceptions.html#exception-hierarchy -- Marc-Andre Lemburg eGenix.com Professional Python Services directly from the Experts (#1, May 01 2020)
Python Projects, Coaching and Support ... https://www.egenix.com/ Python Product Development ... https://consulting.egenix.com/
::: We implement business ideas - efficiently in both time and costs ::: eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611 https://www.egenix.com/company/contact/ https://www.malemburg.com/
M.-A. Lemburg wrote:
Hi Ram,
I think you are confusing the exception type with the exception reason. By adding 100 more exception types, you don't make things easier, but instead you complicate things, since we'd all have to memorize those 100 exception types.
That said, enhancing the error reasons texts is certainly something that would help everyone.
Adding more exception types to the stack makes sense when there's a dedicated need to catch only specific sub types, but even there it's already possible to add this extra information to the exception objects as e.g. .errno or similar attribute.
There is prior art, though, specifically for errno: $ python2 -c 'open("not-there")' Traceback (most recent call last): File "<string>", line 1, in <module> IOError: [Errno 2] No such file or directory: 'not-there' $ python3 -c 'open("not-there")' Traceback (most recent call last): File "<string>", line 1, in <module> FileNotFoundError: [Errno 2] No such file or directory: 'not-there' I very much prefer the specific exception because most of the time I only want to handle the common problems. Instead of try: open file except OSError as err: if err.errno == what was file-not-found? ah ENOENT: # deal with it elif err.errno == what was is-a-directory? ah EISDIR: # deal with it else: # give up raise I can now write try: open file except FileNotFoundError: # deal with it except IsADirectoryError: # deal with it which (a) looks better to me and (b) is easier to remember. It's a bit like Guido's advice to use distinct functions instead of flags. I am sure that I would quickly adopt more specific exceptions in other areas (most notably HTTPError). I don't think they all need to become built-ins.
On 01.05.2020 11:40, Peter Otten wrote:
M.-A. Lemburg wrote:
Hi Ram,
I think you are confusing the exception type with the exception reason. By adding 100 more exception types, you don't make things easier, but instead you complicate things, since we'd all have to memorize those 100 exception types.
That said, enhancing the error reasons texts is certainly something that would help everyone.
Adding more exception types to the stack makes sense when there's a dedicated need to catch only specific sub types, but even there it's already possible to add this extra information to the exception objects as e.g. .errno or similar attribute.
There is prior art, though, specifically for errno:
$ python2 -c 'open("not-there")' Traceback (most recent call last): File "<string>", line 1, in <module> IOError: [Errno 2] No such file or directory: 'not-there' $ python3 -c 'open("not-there")' Traceback (most recent call last): File "<string>", line 1, in <module> FileNotFoundError: [Errno 2] No such file or directory: 'not-there'
Yes, and the reason for having those is that you will want to often catch those exceptions in applications in a platform independent way. That doesn't apply to all OS errors, though. As with all great new features, a case has to be made why an addition is necessary, what the benefits and drawbacks are and why we should force millions of Python programmers to learn about the addition. Note that we're talking about the Python core, not an application or Python package from PyPI. Those can, of course, define their own exceptions and hierarchies. I assume Ram was testing waters here... just to get an idea of what it takes for such a change to go in, please see the PEP for the OS/IO exception hierarchy changes: https://www.python.org/dev/peps/pep-3151/ -- Marc-Andre Lemburg eGenix.com Professional Python Services directly from the Experts (#1, May 01 2020)
Python Projects, Coaching and Support ... https://www.egenix.com/ Python Product Development ... https://consulting.egenix.com/
::: We implement business ideas - efficiently in both time and costs ::: eGenix.com Software, Skills and Services GmbH Pastor-Loeh-Str.48 D-40764 Langenfeld, Germany. CEO Dipl.-Math. Marc-Andre Lemburg Registered at Amtsgericht Duesseldorf: HRB 46611 https://www.egenix.com/company/contact/ https://www.malemburg.com/
On Fri, May 1, 2020 at 1:38 AM M.-A. Lemburg <mal@egenix.com> wrote:
Adding more exception types to the stack makes sense when there's a dedicated need to catch only specific sub types, but even there it's already possible to add this extra information to the exception objects as e.g. .errno or similar attribute.
Maybe it's too late for this, but I would love it if ".errno or similar" were more standardized. As it is, every exception may have it's own way to find out more about exactly what caused it, and often you are left with parsing the message if you really want to know. Honestly, I've never written production code that does that -- but I don't think that's because there's no need for it, but because, well, parsing an error message is pretty painful, and just seems "wrong". I'm not sure how this could reasonably work, but maybe we could standardize that all Exceptions have an .errno attribute, and a standard mapping between the errorno and a message, or, .... Even if most Exceptions would have only a single error number, this would be a standardized way to add extra info to any Exception. And frankly I've always thought it odd that the way to get the message was to grab the first element in .args -- it's a defacto standard, but wouldn't it be better to make it a actual standard? Not well thought out, but this may be a way to address Ram's issues, while not massively cluttering up the Exception namespace. NOTE: I'm not sure where I come down on this, but having to "memorize" a bunch more exceptions does not concern me in the least. I pretty much always write code or tests that trigger the Exception I'm going to catch to make sure I've got the right one anyway. Sure, I'm pretty familiar with ValueError and TypeError, but there are a lot more than I can keep in my brain at one time anyway, and I need to spell it right, and all that. The one complication I DO see to this approach is that we'd have a lot of Exceptions that were subclasses of more general ones, and when you see it, it would not be obvious what the hierarchy is, so folks would tend to use the fine-grained exception over the more general one, even if that wasn't appropriate. -CHB -- Christopher Barker, PhD Python Language Consulting - Teaching - Scientific Software Development - Desktop GUI and Web Development - wxPython, numpy, scipy, Cython
On Fri, May 1, 2020 at 1:24 PM Christopher Barker <pythonchb@gmail.com> wrote:
On Fri, May 1, 2020 at 1:38 AM M.-A. Lemburg <mal@egenix.com> wrote:
Adding more exception types to the stack makes sense when there's a dedicated need to catch only specific sub types, but even there it's already possible to add this extra information to the exception objects as e.g. .errno or similar attribute.
Maybe it's too late for this, but I would love it if ".errno or similar" were more standardized. As it is, every exception may have it's own way to find out more about exactly what caused it, and often you are left with parsing the message if you really want to know.
Honestly, I've never written production code that does that -- but I don't think that's because there's no need for it, but because, well, parsing an error message is pretty painful, and just seems "wrong".
I'm not sure how this could reasonably work, but maybe we could standardize that all Exceptions have an .errno attribute, and a standard mapping between the errorno and a message, or, ....
+100 With the exception of the obscure "SyntaxError: Invalid syntax", this would make it really easy to provide translations of error messages in other languages (or in "beginner friendly English") as I'm slowly doing with friendly-traceback. André Roberge
On May 1, 2020, at 09:24, Christopher Barker <pythonchb@gmail.com> wrote:
Maybe it's too late for this, but I would love it if ".errno or similar" were more standardized. As it is, every exception may have it's own way to find out more about exactly what caused it, and often you are left with parsing the message if you really want to know.
I don’t think there are many cases where a standardized .errno would help—and I think most such cases would be better served by separate exceptions. With OSError, errno was a problem to be fixed, not an ideal solution to emulate everywhere. You do often need to be able to get more information, and that is a problem, but I think it usually needs to be specific to each exception, not something generic. Does code often need to distinguish between an unpacking error and an int parsing error? If so, you should be able to handle UnpackingError and IntParsingError, not handle ValueError and check an .errno against some set of dozens of new builtin int constants. If not, then we shouldn’t change anything at all. As for parsing the error message, that usually comes up because there’s auxiliary information that you need but that isn’t accessible. For example, in 2.x, to get the filename that failed to open, you had to regex .args[0], and that sucked. But the fix was to add a .filename to all of the relevant exceptions, and now it’s great. If you need to be able to get the failing string for int(s) raising a ValueError today, you have to regex .args[0], and that sucks. Do people actually need to do that? If so, there should be a .string or something that carries that information; an .errno won’t help. It seems like every year or two, someone suggests that we should go through the stdlib and fix all the exceptions to be reasonably distinguishable and to make their relevant information more accessible, and I don’t think anyone ever has a problem with that, it’s just that nobody’s ever willing to volunteer to survey every place a builtin or stdlib raises, list them all, and work out exactly what should be changed and where.
On Fri, May 1, 2020 at 12:28 PM Andrew Barnert <abarnert@yahoo.com> wrote:
Maybe it's too late for this, but I would love it if ".errno or similar" were more standardized. As it is, every exception may have it's own way to find out more about exactly what caused it, and often you are left with parsing the message if you really want to know.
I don’t think there are many cases where a standardized .errno would help—and I think most such cases would be better served by separate exceptions. With OSError, errno was a problem to be fixed, not an ideal solution to emulate everywhere.
I will note that wrote is response to someone else suggesting that that was the solution to the OP's problem. If the "policy" is that some information should be tacked on to a fairly generic exception, rather than crating a bunch of subclasses if that exception, then I'm suggesting that there should be a standardized way to do that.
You do often need to be able to get more information, and that is a problem, but I think it usually needs to be specific to each exception, not something generic.
but does that mean there can't be a more standardized way to "add more information" maybe not -- I can't say I've thougth deeply about it, but we could go ONE step better: as far as I can tell, the only thing at all a standard is the exceptions all have a .args attribute wich is a tuple of ??? IN practic,e the zeroth element of that tuple is the "Error Message", but it could be any old thing. Python2 DID have a .message attribute -- I guess I should go look and find documentation for the reasoning behind that, but it does seem like a step backwards to me. As for parsing the error message, that usually comes up because there’s
auxiliary information that you need but that isn’t accessible. For example, in 2.x, to get the filename that failed to open, you had to regex .args[0], and that sucked. But the fix was to add a .filename to all of the relevant exceptions, and now it’s great. If you need to be able to get the failing string for int(s) raising a ValueError today, you have to regex .args[0], and that sucks. Do people actually need to do that? If so, there should be a .string or something that carries that information; an .errno won’t help.
but it does seem like this could be a bit more standardized. in those two cases, you have the data passed in that caused the failure -- could they bwoth use .input_value or something? Maybe it's better to have a use-specific name, maybe not. I'm not going to be able to motivate this well, so I should probably just stop until/unless I get some more time to dig back in my archives and find examples when this was an issue, but I know that every time I've wanted a bit more info from an Exception, I realized that .args is the ONLY standard place for any info -- and thought "really?" It seems like every year or two, someone suggests that we should go through
the stdlib and fix all the exceptions to be reasonably distinguishable and to make their relevant information more accessible, and I don’t think anyone ever has a problem with that, it’s just that nobody’s ever willing to volunteer to survey every place a builtin or stdlib raises, list them all, and work out exactly what should be changed and where.
Probably so, but it seems that no one's going to do that work unless they think it will be accepted -- kin dof like how Ram proposed this in vague and hyperbolic terms, rather than providing a big list of all the places he thought needed improvement. But it seems clear that "doing a big revamp if all the Exceptions and adding alot more subclasses" is not supported. Which doesn't means that a few more expansions wouldn't be excepted. So folks that like this idea may be best served by finding the lowest hanging fruit, and suggesting jsut a few. -CHB -- Christopher Barker, PhD Python Language Consulting - Teaching - Scientific Software Development - Desktop GUI and Web Development - wxPython, numpy, scipy, Cython
On May 1, 2020, at 14:34, Christopher Barker <pythonchb@gmail.com> wrote:
But it seems clear that "doing a big revamp if all the Exceptions and adding alot more subclasses" is not supported. Which doesn't means that a few more expansions wouldn't be excepted.
So folks that like this idea may be best served by finding the lowest hanging fruit, and suggesting jsut a few.
I think you’re right. It _might_ be accepted if someone did the work, but it’s probably a lot easier to get separate small changes with solid use cases in one by one. As long as you’re not being sneaky and pretending like you don’t have a master plan, and each of the changes is convincing, I think they’d have a much better chance. And there ought to be good use cases for “these builtin parse functions should have a .string with the input that failed so you don’t have to regex it out of the message” or “I need to distinguish this one kind of ValueError from all the other kinds” or whatever; a lot easier to argue for those use cases than something abstract and general. And almost any way it turns out seems like a win. Even if they all get rejected, better to know you were on the wrong track early rather than after a hundred hours of work. Or if it turns out to be more work than you expected and you get sick of doing it, at least you’ve improved some of the most important cases. Or maybe you’d just keep doing it and people just keep saying “fine”. Or maybe someone says, “Hold on, another one of these? They’re all good on their own, but shouldn’t we have some master plan behind it all?” and then you can point back to the master plan you posted in this thread that nobody wanted to read at the time, and now they’ll want to read it and start bikeshedding. :) (By “you” here I don’t mean you, Christopher; I mean Ram, or whoever else wants to do all this work.) By the way:
Python2 DID have a .message attribute -- I guess I should go look and find documentation for the reasoning behind that, but it does seem like a step backwards to me.
In 2.6 and 2.7, it’s undocumented, and should always be either the same thing __str__ returns or the empty string. So, not particularly useful. I believe it exists as a consequence of the first time someone suggested “let’s clean up all the exceptions” but then that cleanup didn’t get started. It was added in 2.5, along with a planned deprecation of args, and a new rule for __str__ (return self.message instead of formatting self.args), and a new idiom for how newly-written exception classes should super: don’t pass *args, pass a single formatted string; anything worth keeping around for users is worth storing in a nicely named attribute the way SyntaxError and IOError always have. And Py3000 was going to change all the existing exceptions to use that new idiom. But that never happened, and 2.6 and 3.0 basically went back to 2.4: there’s no mention of message at all, args isn’t going to be deprecated, the rule for __str__ is the old one, etc. There are more custom attributes on more exceptions than there used to be, but they seem to mostly have grown on a case by case basis (and mostly on brand new exceptions) rather than in one fell swoop. Which implies that you were right about the best way to get anything done.
On Fri, May 01, 2020 at 12:28:02PM -0700, Andrew Barnert via Python-ideas wrote:
On May 1, 2020, at 09:24, Christopher Barker <pythonchb@gmail.com> wrote:
Maybe it's too late for this, but I would love it if ".errno or similar" were more standardized. As it is, every exception may have it's own way to find out more about exactly what caused it, and often you are left with parsing the message if you really want to know.
I don’t think there are many cases where a standardized .errno would help—and I think most such cases would be better served by separate exceptions. With OSError, errno was a problem to be fixed, not an ideal solution to emulate everywhere.
You do often need to be able to get more information, and that is a problem, but I think it usually needs to be specific to each exception, not something generic.
Does code often need to distinguish between an unpacking error and an int parsing error? If so, you should be able to handle UnpackingError and IntParsingError, not handle ValueError and check an .errno against some set of dozens of new builtin int constants. If not, then we shouldn’t change anything at all.
As for parsing the error message, that usually comes up because there’s auxiliary information that you need but that isn’t accessible. For example, in 2.x, to get the filename that failed to open, you had to regex .args[0], and that sucked.
Why would you parse the error message when you already have the file name? try: f = open(filename) except IOError as err: print(filename) I'm trying to think of a case where you might be making a call to open but you don't know the filename. I thought it might be if you pass a file descriptor, but in that case the filename attribute seems to be set to None.
It seems like every year or two, someone suggests that we should go through the stdlib and fix all the exceptions to be reasonably distinguishable and to make their relevant information more accessible, and I don’t think anyone ever has a problem with that,
I do! Christopher's proposal of a central registry of error numbers and standardised error messages just adds a lot more work to every core developer for negligible or zero actual real world benefit. When I want to raise something in the stdlib, I just raise: # let's say I add a kurtosis function raise StatisticsError('not enough data points for kurtosis') Under Christopher's proposal, I have to: * look up the central registry; * find the next ID unused number; * register it to my library and error message; * use the relevent errno and the same error message in my code: # something like this? raise StatisticsError('not enough data points for kurtosis', errno=926361) and while I'm doing that, one of the faster and more productive core devs have come along and used that errno before I could merge my patch, so now I have to change the errno. And then if I decide to improve the error message, what now? Do I have to register a new errno? What if the error message is variable? Expected at least 4 data points, but got 2. And the benefit of this is ... what? I'd like to see the real-world concrete benefit that couldn't be more easily accomplished by using a separate try...except around *only* the call to kurtosis: try: k = kurtosis(data) except StatisticsError: # This can only mean one thing: not enough data. k = None I'm sorry to anyone who likes to use a single try around many computations: try: xm = mean(ydata) ym = mean(ydata) xsd = stdev(xdata) ysd = stdev(ydata) except StatisticsError as err: # How do I distinguish which call failed? but supporting that is not something I'm prepared to do until either I need that functionality myself or somebody provides a really compelling use-case, not just "it would be cool if..." :-) -- Steven
On May 1, 2020, at 16:32, Steven D'Aprano <steve@pearwood.info> wrote:
On Fri, May 01, 2020 at 12:28:02PM -0700, Andrew Barnert via Python-ideas wrote:
On May 1, 2020, at 09:24, Christopher Barker <pythonchb@gmail.com> wrote: Maybe it's too late for this, but I would love it if ".errno or similar" were more standardized. As it is, every exception may have it's own way to find out more about exactly what caused it, and often you are left with parsing the message if you really want to know. I don’t think there are many cases where a standardized .errno would help—and I think most such cases would be better served by separate exceptions. With OSError, errno was a problem to be fixed, not an ideal solution to emulate everywhere. You do often need to be able to get more information, and that is a problem, but I think it usually needs to be specific to each exception, not something generic. Does code often need to distinguish between an unpacking error and an int parsing error? If so, you should be able to handle UnpackingError and IntParsingError, not handle ValueError and check an .errno against some set of dozens of new builtin int constants. If not, then we shouldn’t change anything at all. As for parsing the error message, that usually comes up because there’s auxiliary information that you need but that isn’t accessible. For example, in 2.x, to get the filename that failed to open, you had to regex .args[0], and that sucked.
Why would you parse the error message when you already have the file name?
try: f = open(filename) except IOError as err: print(filename)
try: config = parse_config() except IOError as err: print(filename) You can’t get the local variable out of some other function that you called, even with frame hacking. At any rate, it’s a bit silly to relitigate this change. All of the new IOError subclasses where a filename is relevant have had a filename attribute since 3.0, so this problem has been solved for over a decade. If you really prefer the 2.x situation where sometimes those exception instances had the filename and sometimes not, you’ll need a time machine.
It seems like every year or two, someone suggests that we should go through the stdlib and fix all the exceptions to be reasonably distinguishable and to make their relevant information more accessible, and I don’t think anyone ever has a problem with that,
I do!
Christopher's proposal of a central registry of error numbers and standardised error messages just adds a lot more work to every core developer for negligible or zero actual real world benefit.
You’re replying to a message saying “errno was a problem to be fixed, not an ideal solution to emulate” and likewise having to parse errors. And you’re insisting that you disagree because adding errno and standardizing messages so they could be parsed would be a problem for maintainers as well as for users. Sure, you’re right, but that’s not in any way an argument against Ram’s proposal, or against the paragraph you quoted; if anything, it’s an argument *for* it.
On Fri, May 01, 2020 at 06:59:01PM -0700, Andrew Barnert wrote:
For example, in 2.x, to get the filename that failed to open, you had to regex .args[0], and that sucked.
Why would you parse the error message when you already have the file name?
try: f = open(filename) except IOError as err: print(filename)
try: config = parse_config() except IOError as err: print(filename)
That's not a very convincing example, because surely if you are logging the error, or reporting it to the user, you need the entire error message. As a user, what am I to make of this? /tmp/z when you could show me this instead? FileNotFoundError: [Errno 2] No such file or directory: '/tmp/z' I might also argue that this is a poorly designed parse_config function for many reasons, but it's obvious to me that you intended it as only a toy example to match my toy example, not real-world code. But that's the thing, there are many things which seem compelling when you only look at toy examples, but in real-world code they become less useful. For example, in a real-world config system, you probably have "read_config" try a series of multiple file names, in some specified order, rather than fail, and the config function itself logs the errors if you need them logged. Even if none of them are available, you ought to return default settings rather than fail. (I've used applications that die if a config file isn't readable, and they are horrible to use. But I digress.) In the standard library, my quick and dirty grep has found a few uses for err.filename in the stdlib: - ten cases testing that it is set correctly; - one place where it appears to be copied from one exception to another; and unless I've missed anything, that's it. Take from that what you will. So in the spirit of always being open to learning something new, have you personally used the err.filename attribute, and if so, under what circumstances was it useful to you? [...]
At any rate, it’s a bit silly to relitigate this change.
Was that what I was doing? Whew, thank goodness you told me, now I can cancel the PEP I was writing to deprecate and remove that feature! *wink* But seriously... unless it is your position that every functional change made to the language and stdlib is by definition optimal, and therefore beyond question, I don't think it is "silly" to revist a change made a decade ago and see whether it actually turned out to be as useful as we hoped, *before* using that change as an example of a successful language feature that ought to be copied. YAGNI is a genuine principle, and I cannot tell you how many times I've started custom exceptions and loaded them up with information that I surely would need to extract programmatically, only to discover that all I really needed was to read the error message. So please forgive me if I'm a bit cautious about the suggestion that we must load exceptions up with more information.
All of the new IOError subclasses where a filename is relevant have had a filename attribute since 3.0, so this problem has been solved for over a decade.
Since the filename attribute can be None, you still have to deal with the case where the filename is missing. (Even if it is rarer than it used to be.)
If you really prefer the 2.x situation where sometimes those exception instances had the filename and sometimes not, you’ll need a time machine.
Since I've never been in a position where I needed to extract the file name from an exception, at least not as far as I can remember, I don't think I would care if it was missing or not. YMMV.
It seems like every year or two, someone suggests that we should go through the stdlib and fix all the exceptions to be reasonably distinguishable and to make their relevant information more accessible, and I don’t think anyone ever has a problem with that,
I do!
Christopher's proposal of a central registry of error numbers and standardised error messages just adds a lot more work to every core developer for negligible or zero actual real world benefit.
You’re replying to a message saying “errno was a problem to be fixed,
Not every obscure OS error should be given its own exception. It is good that the most common ones have been, like FileNotFoundError, but for rarer and more exotic exceptions, it isn't worth the bloat of adding 50 exception subclasses which most people will never experience in their entire lifetime. So for those, a standard errno works great and I don't think it is a problem to be fixed. In fact, I don't think it was a problem as such, it is just that separate exceptions for common failures was *even better*.
not an ideal solution to emulate” and likewise having to parse errors. And you’re insisting that you disagree because adding errno and standardizing messages so they could be parsed would be a problem for maintainers as well as for users. Sure, you’re right, but that’s not in any way an argument against Ram’s proposal, or against the paragraph you quoted; if anything, it’s an argument *for* it.
I'm not commenting on Ram's proposal here. I'm commenting on the subthread where Christopher proposed a central registry of error numbers. If you take Christopher's proposal as one of those suggestions to "fix all the exceptions to be reasonably distinguishable and to make their relevant information more accessible" (your words), as I did, then I am explicitly disagreeing with your comment than nobody has ever had a problem with that. "Make exceptions better" is one of those over-generic feel-good things that everyone can agree on. Who would want them to be worse? But when you get down to details, if the way we make exceptions "reasonably distinguishable" is with a central registry of error numbers, then I need to be convinced that it is (1) useful and (2) workable in practice. Hmmm... maybe it could be workable... let me think about that... -- Steven
On Fri, May 01, 2020 at 09:23:20AM -0700, Christopher Barker wrote:
I'm not sure how this could reasonably work, but maybe we could standardize that all Exceptions have an .errno attribute, and a standard mapping between the errorno and a message, or, ....
Even if most Exceptions would have only a single error number, this would be a standardized way to add extra info to any Exception.
Okay, I've given this a bit more thought and I think that I have a way to make this -- maybe -- practical without having to have a universal global registry. There will still be a registry, but it will be per- installation, and generated dynamically when exceptions are raised. Whether it's useful, I don't know. (That's a polite way of saying I don't think it is *wink* ) Whenever an exception is instantiated: ValueError('too many chromions in the neutron flux') `BaseException` looks for the combination of exception type and error message in a per-site database. If they are not already in the DB, it generates a new unique number for them. Once the error number is generated or retrieved, it is added to the exception instance. Exceptions like StopIteration will need to opt-out of this costly process. That's easy enough: have the `BaseException.__init__` method call a public `register` method to do the work. If you want to opt-out, just set `register = None` in your class. If one wanted to be a bit fancier, in a "Do What I Mean" kind of way, we could use string similarity rather than equality, so that minor typos in the error message won't be considered significant. The downside of that is that ValueError('too many chromions in the neutron flux') ValueError('too few chromions in the neutron flux') will probably count as being similar (edit distance of 1 word), which you may not want. Being site-specific, the error numbers won't necessarily correspond between one site and the next. So you can't google for error numbers. There would have to be some sort of tool for looking them up in the database. I'm not entirely sure what we would do with this error number once you have it, but there it is. It was fun to think about. If anyone wants to experiment with this, you can use a mixin class in your exceptions without having to touch BaseException. -- Steven
Recent related discussion: https://mail.python.org/archives/list/python-ideas@python.org/thread/MXPCNEA... On Fri, May 1, 2020 at 8:53 AM Ram Rachum <ram@rachum.com> wrote:
Hi,
Here's something I wanted in Python for many years. If this has been discussed in the past, please refer me to that discussion.
On one hand, it's something that I can't imagine the python-dev community supporting. On the other hand, it would maintain backward compatibility.
I wish there were a 100 more built-in exceptions in Python, that will be very specific about what went wrong.
If I do this:
>>> x, y = range(3)
I know it'll raise a ValueError, because I've memorized that, but it did take me a few years to remember where I should expect ValueError and where I should expect TypeError.
It would be nice if the operation above raised UnpackingOverflowError, which will be a subclass of UnpackingError, along with UnpackingUnderflowError. UnpackingError can be a subclass of ValueError, for backward compatibility.
Similarly, if I did this:
>>> def f(x, y): return x + y >>> f(1)
I would get a TypeError. Would be a lot cooler if I got MissingArgumentsError, which would be a subclass of SignatureError, which would be a subclass of TypeError.
There are 2 reasons I want this:
1. When I'm writing a try..except clause, I want to catch a specific exception like MissingArgumentsError rather than ValueError or TypeError. They're too ubiquitous. I don't want some other unexpected failure producing the same ValueError and triggering my except clause.
2. When I get an error, especially from some shitty corporate system that truncates the traceback, I want to get as many hints as possible about what went wrong.
It's true that today, most Python exceptions have good text in their message, like "TypeError: f() missing 1 required positional argument: 'y'". But that isn't guaranteed everywhere, and specific exception types could help.
What do you think?
Ram. _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/XZXWXI... Code of Conduct: http://python.org/psf/codeofconduct/
Is "100 more exceptions" hyperbole? Or do you actually mean precisely one hundred more exceptions? On Fri, May 01, 2020 at 09:48:21AM +0300, Ram Rachum wrote: [...]
I know it'll raise a ValueError, because I've memorized that, but it did take me a few years to remember where I should expect ValueError and where I should expect TypeError.
If it took you a few years to learn that ValueError is raised for bad values, and TypeError for bad types, then it would probably take you three or four hundred years to memorise an additional 100 exceptions and their spellings. "Is it UnpackingOverflowException or PackingUnderflowError or TooManyValuesUnpackException or ValueUnpackingError or ...???" It took me far too long to learn the difference between UnicodeEncodingError and UnicodeDecodingError. Or is it UnicodeEncodeError and UnicodeDecodeError? It is possible to have too many fine-grained errors as well as too few. [...]
Similarly, if I did this:
>>> def f(x, y): return x + y >>> f(1)
I would get a TypeError. Would be a lot cooler if I got MissingArgumentsError, which would be a subclass of SignatureError, which would be a subclass of TypeError.
This is an idea I could get behind: https://mail.python.org/archives/list/python-ideas@python.org/message/MXPCNE...
There are 2 reasons I want this:
1. When I'm writing a try..except clause, I want to catch a specific exception like MissingArgumentsError rather than ValueError or TypeError. They're too ubiquitous. I don't want some other unexpected failure producing the same ValueError and triggering my except clause.
Best practice is to put try...except around only a *single* operation which may raise what you want to catch. Of course that's easier said than done, especially since nearly anything can raise nearly anything. But in practice, it is surprisingly more practical than it sounds in theory.
2. When I get an error, especially from some shitty corporate system that truncates the traceback, I want to get as many hints as possible about what went wrong.
I sympathise, but it's not up to the language to work around the bad practices of lousy corporate systems.
It's true that today, most Python exceptions have good text in their message, like "TypeError: f() missing 1 required positional argument: 'y'". But that isn't guaranteed everywhere, and specific exception types could help.
What do you think?
I think that speaking generically, being in favour of "better error messages" and "more fine-grained exceptions" is like being in favour of peace. But the devil is in the details. Peace at what cost? -- Steven
On Fri, May 1, 2020 at 12:28 PM Steven D'Aprano <steve@pearwood.info> wrote:
Is "100 more exceptions" hyperbole? Or do you actually mean precisely one hundred more exceptions?
It is hyperbole. I should have realized that the way I phrased it was provocative. Maybe people would have responded better if I put more thought into that... 100 is an arbitrary number of course. I gave a few specific examples of UnpackingUnderflowError and MissingArgumentsError, and if I spent 30 minutes on it I could probably come up with 10 more examples. The point isn't the specific example, but to advocate for a general strategy of breaking down exception to as many classes as possible, instead of having this fight over and over again on each exception.
This is an idea I could get behind:
https://mail.python.org/archives/list/python-ideas@python.org/message/MXPCNE...
Alex sent me that, I'm happy to see it :)
On 05/01/2020 02:19 AM, Steven D'Aprano wrote:
Best practice is to put try...except around only a *single* operation which may raise what you want to catch. Of course that's easier said than done, especially since nearly anything can raise nearly anything.
The follow-on to that is to only catch what you can recover from.
But in practice, it is surprisingly more practical than it sounds in theory.
Since not every problem can be recovered from and therefore shouldn't be caught. -- ~Ethan~
On Fri, 1 May 2020 at 10:29, Steven D'Aprano <steve@pearwood.info> wrote: [...]
"Is it UnpackingOverflowException or PackingUnderflowError or TooManyValuesUnpackException or ValueUnpackingError or ...???"
It took me far too long to learn the difference between UnicodeEncodingError and UnicodeDecodingError. Or is it UnicodeEncodeError and UnicodeDecodeError?
It is possible to have too many fine-grained errors as well as too few.
This is thinking about exceptions and the proposal too simply. When I read this, I (a) found out that this exception is a built-in so it isn't hard to find out which is which (any IDE or IPython would help) but more importantly, looking at their MRO, if you cannot understand the difference, and you don't have to, you could instead choose to catch UnicodeError instead (or the combination of both). However, if you just want to catch encoding errors you can, and this is the crux of this proposal: allowing similar catches throughout the standard library as a first class citizen seems sensible to me and I am +1 of this proposal.
On 5/2/2020 6:00 AM, Henk-Jaap Wagenaar wrote:
On Fri, 1 May 2020 at 10:29, Steven D'Aprano <steve@pearwood.info <mailto:steve@pearwood.info>> wrote: [...]
"Is it UnpackingOverflowException or PackingUnderflowError or TooManyValuesUnpackException or ValueUnpackingError or ...???"
It took me far too long to learn the difference between UnicodeEncodingError and UnicodeDecodingError. Or is it UnicodeEncodeError and UnicodeDecodeError?
It is possible to have too many fine-grained errors as well as too few.
This is thinking about exceptions and the proposal too simply. When I read this, I (a) found out that this exception is a built-in so it isn't hard to find out which is which (any IDE or IPython would help) but more importantly, looking at their MRO, if you cannot understand the difference, and you don't have to, you could instead choose to catch UnicodeError instead (or the combination of both).
However, if you just want to catch encoding errors you can, and this is the crux of this proposal: allowing similar catches throughout the standard library as a first class citizen seems sensible to me and I am +1 of this proposal.
Please show an example of where you would catch ValueUnpackingError and how you'd handle it (recover, re-raise, whatever). Eric
Of course, there are other ways of writing this code, but imagine this for a database interface where a save normally returns the saved object (inspired by Django) ``` try: x, y = Foo.save() except ValueUnpackingError: // oh... this means saving failed (it returned None instead) // let's return a sensible response return ExceptionToResponse(FooStateToException(foo.state)) ``` Another example would be an interactive session where you expect an iterable to have 3 entries (say for a 3D space coordinate) and you want to check this specifically: ``` try: x, y, z = point except ValueUnpackingError: print(f"point has wrong dimension") ``` where point is an instance of: ``` class Point: def __iter__(self): return (float(x) for x in self._internal) ``` Can you spot here why ValueError would result in significantly different behaviour? ValueError will also catch it if point's internal list contains a bad entry (e.g. a string that does not convert to a float). Having these exceptions does not take anything away as they can choose the more general version. On Sat, 2 May 2020 at 11:12, Eric V. Smith <eric@trueblade.com> wrote:
On 5/2/2020 6:00 AM, Henk-Jaap Wagenaar wrote:
On Fri, 1 May 2020 at 10:29, Steven D'Aprano <steve@pearwood.info> wrote: [...]
"Is it UnpackingOverflowException or PackingUnderflowError or TooManyValuesUnpackException or ValueUnpackingError or ...???"
It took me far too long to learn the difference between UnicodeEncodingError and UnicodeDecodingError. Or is it UnicodeEncodeError and UnicodeDecodeError?
It is possible to have too many fine-grained errors as well as too few.
This is thinking about exceptions and the proposal too simply. When I read this, I (a) found out that this exception is a built-in so it isn't hard to find out which is which (any IDE or IPython would help) but more importantly, looking at their MRO, if you cannot understand the difference, and you don't have to, you could instead choose to catch UnicodeError instead (or the combination of both).
However, if you just want to catch encoding errors you can, and this is the crux of this proposal: allowing similar catches throughout the standard library as a first class citizen seems sensible to me and I am +1 of this proposal.
Please show an example of where you would catch ValueUnpackingError and how you'd handle it (recover, re-raise, whatever).
Eric
_______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/I26BBA... Code of Conduct: http://python.org/psf/codeofconduct/
On Sat, May 2, 2020 at 12:39 PM Henk-Jaap Wagenaar < wagenaarhenkjaap@gmail.com> wrote:
Of course, there are other ways of writing this code, but imagine this for a database interface where a save normally returns the saved object (inspired by Django)
``` try: x, y = Foo.save() except ValueUnpackingError: // oh... this means saving failed (it returned None instead) // let's return a sensible response return ExceptionToResponse(FooStateToException(foo.state)) ```
My concern is that this is risky code. If `Foo.save()` has a bug somewhere inside (or maybe it calls back to your own code which has a bug) it could raise `ValueUnpackingError` for different reasons, and then that gets caught and misleads people into thinking that Foo.save returned None which leads to all sorts of frustration and confusion. This code: ``` result = Foo.save() if result is None: return ExceptionToResponse(...) x, y = result ``` is not sexy, but it's explicit, safe, and it targets the real problem precisely. Creating these new exceptions would encourage people to write code that will bite them later.
Another example would be an interactive session where you expect an iterable to have 3 entries (say for a 3D space coordinate) and you want to check this specifically:
``` try: x, y, z = point except ValueUnpackingError: print(f"point has wrong dimension") ```
where point is an instance of:
``` class Point: def __iter__(self): return (float(x) for x in self._internal) ```
The first example made sense, this is too much of a contrived toy example. Why would you write that try/except in an interactive session? How does catching the exception leave you better off than just letting it bubble up and show you a traceback? What are you going to do after now that x,y,z are undefined variables? How did you end up with a point of unknown dimension? Can you spot here why ValueError would result in significantly different
behaviour? ValueError will also catch it if point's internal list contains a bad entry (e.g. a string that does not convert to a float).
The solution here is to ensure that the strings are converted to floats long before iteration, either by Point or by its producer. That's the kind of solution programmers should look for. Catching exceptions in general should be a last resort, and making it slightly less wrong may do more harm than good.
On 5/2/2020 7:18 AM, Alex Hall wrote:
On Sat, May 2, 2020 at 12:39 PM Henk-Jaap Wagenaar <wagenaarhenkjaap@gmail.com <mailto:wagenaarhenkjaap@gmail.com>> wrote:
Of course, there are other ways of writing this code, but imagine this for a database interface where a save normally returns the saved object (inspired by Django)
``` try: x, y = Foo.save() except ValueUnpackingError: // oh... this means saving failed (it returned None instead) // let's return a sensible response return ExceptionToResponse(FooStateToException(foo.state)) ```
My concern is that this is risky code. If `Foo.save()` has a bug somewhere inside (or maybe it calls back to your own code which has a bug) it could raise `ValueUnpackingError` for different reasons, and then that gets caught and misleads people into thinking that Foo.save returned None which leads to all sorts of frustration and confusion.
This code:
``` result = Foo.save() if result is None: return ExceptionToResponse(...) x, y = result ```
is not sexy, but it's explicit, safe, and it targets the real problem precisely.
Creating these new exceptions would encourage people to write code that will bite them later.
I agree that we don't want to encourage code like the above example with catching the hypothetical ValueUnpackingError. I contend that even if we added ValueUnpackingError, no one would ever write code that used it, but should instead do what they do now: unpack it after checking it (as Alex's example shows). Of course, that's a horrible API, but sometimes you have to call horrible APIs. The fact that the API is bad doesn't mean we should add a way to handle it poorly. The only reason I'd want it to exist (and I don't) is to see it in the REPL. But for that, there's the exception message and the traceback, so a unique exception for this would be useless to me.
Another example would be an interactive session where you expect an iterable to have 3 entries (say for a 3D space coordinate) and you want to check this specifically:
``` try: x, y, z = point except ValueUnpackingError: print(f"point has wrong dimension") ```
where point is an instance of:
``` class Point: def __iter__(self): return (float(x) for x in self._internal) ```
The first example made sense, this is too much of a contrived toy example. Why would you write that try/except in an interactive session? How does catching the exception leave you better off than just letting it bubble up and show you a traceback? What are you going to do after now that x,y,z are undefined variables? How did you end up with a point of unknown dimension?
Can you spot here why ValueError would result in significantly different behaviour? ValueError will also catch it if point's internal list contains a bad entry (e.g. a string that does not convert to a float).
I've never caught an exception in the REPL. And even if I wanted to, like the other examples above, you'd want to put the exception around the smallest possible piece of code. What if the each "x" above was an instance of something whose __float__() might raise ValueUnpackingError? class X: def __float__(self): a, b = [3.0] return a You'd be catching a ValueUnpackingError, but the wrong one. Just adding ever finer-grained exception classes doesn't solve any of the problems I've seen given in this thread. That's not to say improvement is impossible, but it will need to be on a case-by-case basis, with examples that show an actual improvement. Eric
On 2020-05-02 11:59 a.m., Eric V. Smith wrote:
On 5/2/2020 7:18 AM, Alex Hall wrote:
On Sat, May 2, 2020 at 12:39 PM Henk-Jaap Wagenaar <wagenaarhenkjaap@gmail.com <mailto:wagenaarhenkjaap@gmail.com>> wrote:
Of course, there are other ways of writing this code, but imagine this for a database interface where a save normally returns the saved object (inspired by Django)
``` try: x, y = Foo.save() except ValueUnpackingError: // oh... this means saving failed (it returned None instead) // let's return a sensible response return ExceptionToResponse(FooStateToException(foo.state)) ```
My concern is that this is risky code. If `Foo.save()` has a bug somewhere inside (or maybe it calls back to your own code which has a bug) it could raise `ValueUnpackingError` for different reasons, and then that gets caught and misleads people into thinking that Foo.save returned None which leads to all sorts of frustration and confusion.
This code:
``` result = Foo.save() if result is None: return ExceptionToResponse(...) x, y = result ```
is not sexy, but it's explicit, safe, and it targets the real problem precisely.
Creating these new exceptions would encourage people to write code that will bite them later.
I agree that we don't want to encourage code like the above example with catching the hypothetical ValueUnpackingError. I contend that even if we added ValueUnpackingError, no one would ever write code that used it, but should instead do what they do now: unpack it after checking it (as Alex's example shows). Of course, that's a horrible API, but sometimes you have to call horrible APIs. The fact that the API is bad doesn't mean we should add a way to handle it poorly.
The only reason I'd want it to exist (and I don't) is to see it in the REPL. But for that, there's the exception message and the traceback, so a unique exception for this would be useless to me.
how about: result = Foo.save() try: x, y = result except ValueUnpackingError: return ... this would also handle generators (and, the unpacking operation should wrap the generator's ValueUnpackingError (if any) in a RuntimeError so it doesn't conflict with the unpacking operation's API)
On 2020-05-02 1:07 p.m., Steven D'Aprano wrote:
On Sat, May 02, 2020 at 12:50:19PM -0300, Soni L. wrote:
how about:
result = Foo.save() try: x, y = result except ValueUnpackingError: return ...
If you do that, what benefit is ValueUnpackingError over just ValueError?
unpacking (a generator) still doesn't wrap ValueError, and ValueError is raised a lot more than ValueUnpackingError.
On 5/2/2020 12:20 PM, Soni L. wrote:
On 2020-05-02 1:07 p.m., Steven D'Aprano wrote:
On Sat, May 02, 2020 at 12:50:19PM -0300, Soni L. wrote:
result = Foo.save()
how about: try: x, y = result except ValueUnpackingError: return ...
If you do that, what benefit is ValueUnpackingError over just ValueError?
unpacking (a generator) still doesn't wrap ValueError, and ValueError is raised a lot more than ValueUnpackingError.
Has anyone actually had that problem? And if they did, make it: result = tuple(Foo.save()) try: x, y = result except ValueError: ... You could even check the length of result before assigning it if you wanted to do something fancier without exceptions at all. But it seems silly to me. Since I've seen no actual examples of code that would benefit, I'm still -1 on this particular exception, and -1 on a general-purpose addition of exceptions. Eric
On Sat, May 02, 2020 at 01:20:01PM -0300, Soni L. wrote:
On 2020-05-02 1:07 p.m., Steven D'Aprano wrote:
On Sat, May 02, 2020 at 12:50:19PM -0300, Soni L. wrote:
how about:
result = Foo.save() try: x, y = result except ValueUnpackingError: return ...
If you do that, what benefit is ValueUnpackingError over just ValueError?
unpacking (a generator) still doesn't wrap ValueError
Sorry, I don't understand that comment. What do you mean, "wrap"? Unpacking a generator with too many values raises ValueError: py> a, b = (x for x in range(3)) Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueError: too many values to unpack (expected 2)
and ValueError is raised a lot more than ValueUnpackingError.
Why does that matter, and how does that relate to the specific code sample given? Given the line of code `x, y = result`, the only possible way ValueError could be raised is if `result` has the wrong number of items when unpacking. So what benefit does ValueUnpackingError bring to this example? -- Steven
On 2020-05-02 1:45 p.m., Steven D'Aprano wrote:
On Sat, May 02, 2020 at 01:20:01PM -0300, Soni L. wrote:
On 2020-05-02 1:07 p.m., Steven D'Aprano wrote:
On Sat, May 02, 2020 at 12:50:19PM -0300, Soni L. wrote:
how about:
result = Foo.save() try: x, y = result except ValueUnpackingError: return ...
If you do that, what benefit is ValueUnpackingError over just ValueError?
unpacking (a generator) still doesn't wrap ValueError
Sorry, I don't understand that comment. What do you mean, "wrap"?
Unpacking a generator with too many values raises ValueError:
py> a, b = (x for x in range(3)) Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueError: too many values to unpack (expected 2)
and ValueError is raised a lot more than ValueUnpackingError.
Why does that matter, and how does that relate to the specific code sample given?
Given the line of code `x, y = result`, the only possible way ValueError could be raised is if `result` has the wrong number of items when unpacking. So what benefit does ValueUnpackingError bring to this example?
or if iterating result raises ValueError. consider: def foo(): yield from () raise ValueError iterator = foo() x, y = iterator you get a ValueError, but not a ValueUnpackingError.
On 5/2/2020 2:39 PM, Soni L. wrote:
On 2020-05-02 1:45 p.m., Steven D'Aprano wrote:
On Sat, May 02, 2020 at 01:20:01PM -0300, Soni L. wrote:
On 2020-05-02 1:07 p.m., Steven D'Aprano wrote: On Sat, May 02, 2020 at 12:50:19PM -0300, Soni L. wrote:
>> result = Foo.save()
how about: try: x, y = result except ValueUnpackingError: return ...
If you do that, what benefit is ValueUnpackingError over just ValueError?
unpacking (a generator) still doesn't wrap ValueError
Sorry, I don't understand that comment. What do you mean, "wrap"?
Unpacking a generator with too many values raises ValueError:
py> a, b = (x for x in range(3)) Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueError: too many values to unpack (expected 2)
and ValueError is > raised a lot more than ValueUnpackingError.
Why does that matter, and how does that relate to the specific code sample given?
Given the line of code `x, y = result`, the only possible way ValueError could be raised is if `result` has the wrong number of items when unpacking. So what benefit does ValueUnpackingError bring to this example?
or if iterating result raises ValueError.
consider:
def foo(): yield from () raise ValueError
iterator = foo() x, y = iterator
you get a ValueError, but not a ValueUnpackingError.
And as has been said before, what if iterating raises a ValueUnpackingError? Rather than go over the same ground yet again, I'll stop replying to this thread. I remain at -1 for adding this particular exception. Eric
Hey, I just found a good example for the unpacking errors I suggested: def topological_sort(graph: networkx.DiGraph) -> tuple: try: (zero_node,) = (node for node in graph if not graph.neighbors(node)) except TooFewToUnpackError: raise OverdefinedOrdering except TooManyToUnpackError: raise UnderdefinedOrdering ... This is instead of the following code, which I find less elegant: def topological_sort(graph: networkx.DiGraph) -> tuple: zero_nodes = tuple(node for node in graph if not graph.neighbors(node)) if not zero_nodes: raise OverdefinedOrdering elif len(zero_nodes) >= 2: raise UnderdefinedOrdering else: (zero_node,) = zero_nodes Besides elegance, the above code can be optimized with short-circuit logic for the TooManyToUnpackError, assuming that doesn't break backward compatibility. ... On Sun, May 3, 2020 at 1:39 AM Soni L. <fakedme+py@gmail.com> wrote:
On 2020-05-02 7:29 p.m., Steven D'Aprano wrote:
On Sat, May 02, 2020 at 03:39:50PM -0300, Soni L. wrote:
def foo(): yield from () raise ValueError
def foo(): yield from () raise ValueUnpackingError
Does that help?
the idea is that it'd become a RuntimeError when you unpack it. _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/GQVFED... Code of Conduct: http://python.org/psf/codeofconduct/
On Sat, May 9, 2020 at 7:02 PM Ram Rachum <ram@rachum.com> wrote:
Hey,
I just found a good example for the unpacking errors I suggested:
def topological_sort(graph: networkx.DiGraph) -> tuple: try: (zero_node,) = (node for node in graph if not graph.neighbors(node)) except TooFewToUnpackError: raise OverdefinedOrdering except TooManyToUnpackError: raise UnderdefinedOrdering
...
This is instead of the following code, which I find less elegant:
def topological_sort(graph: networkx.DiGraph) -> tuple: zero_nodes = tuple(node for node in graph if not graph.neighbors(node)) if not zero_nodes: raise OverdefinedOrdering elif len(zero_nodes) >= 2: raise UnderdefinedOrdering else: (zero_node,) = zero_nodes
Besides elegance, the above code can be optimized with short-circuit logic for the TooManyToUnpackError, assuming that doesn't break backward compatibility.
The elegance argument doesn't convince me, you'll need to explain what you mean by the last bit about short-circuit logic.
On Sat, May 9, 2020 at 8:09 PM Alex Hall <alex.mojaki@gmail.com> wrote:
On Sat, May 9, 2020 at 7:02 PM Ram Rachum <ram@rachum.com> wrote:
Besides elegance, the above code can be optimized with short-circuit logic for the TooManyToUnpackError, assuming that doesn't break backward compatibility.
The elegance argument doesn't convince me, you'll need to explain what you mean by the last bit about short-circuit logic.
Ah, I did a test and saw that Python does do that optimization! I meant, that if there are too many values to unpack, we stop hitting the iterator after we surpassed the number of items by 1, rather than consume the whole thing. That is not true about the second example.
On 01/05/2020 07:48, Ram Rachum wrote:
There are 2 reasons I want this:
1. When I'm writing a try..except clause, I want to catch a specific exception like MissingArgumentsError rather than ValueError or TypeError. They're too ubiquitous. I don't want some other unexpected failure producing the same ValueError and triggering my except clause.
If you want to catch MissingArgumentsError, you're doing something really weird. I'm trying to think of a realistic example and failing.
2. When I get an error, especially from some shitty corporate system that truncates the traceback, I want to get as many hints as possible about what went wrong.
Then you should read the exception reason. You're still going to have to think about what your program logic error is, and a separate exception is going to be less use than an explanatory text string. -- Rhodri James *-* Kynesim Ltd
On Fri, May 1, 2020 at 10:15 AM Rhodri James <rhodri@kynesim.co.uk> wrote:
On 01/05/2020 07:48, Ram Rachum wrote:
There are 2 reasons I want this:
1. When I'm writing a try..except clause, I want to catch a specific exception like MissingArgumentsError rather than ValueError or TypeError. They're too ubiquitous. I don't want some other unexpected failure producing the same ValueError and triggering my except clause.
If you want to catch MissingArgumentsError, you're doing something really weird. I'm trying to think of a realistic example and failing.
This type of error is already caught and identified by Python as a TypeError in at least two different situations with the following (generic) error messages: X takes Y positional arguments but Z was given X missing 2 required positional arguments: Y and Z The fact that two such messages exist is indicative that this type of error do occur in realistic code. So, in theory, one could think of extracting these two subcases of TypeError into a new exception subclass; personnally, I think that the current error messages are sufficient and adding a new exception subclass for them would just make the exception handling code more complicated than necessary. André Roberge
2. When I get an error, especially from some shitty corporate system that truncates the traceback, I want to get as many hints as possible about what went wrong.
Then you should read the exception reason. You're still going to have to think about what your program logic error is, and a separate exception is going to be less use than an explanatory text string.
-- Rhodri James *-* Kynesim Ltd _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/4ZKBKM... Code of Conduct: http://python.org/psf/codeofconduct/
On 5/1/2020 9:21 AM, André Roberge wrote:
On Fri, May 1, 2020 at 10:15 AM Rhodri James <rhodri@kynesim.co.uk <mailto:rhodri@kynesim.co.uk>> wrote:
On 01/05/2020 07:48, Ram Rachum wrote: > There are 2 reasons I want this: > > 1. When I'm writing a try..except clause, I want to catch a specific > exception like MissingArgumentsError rather than ValueError or TypeError. > They're too ubiquitous. I don't want some other unexpected failure > producing the same ValueError and triggering my except clause.
If you want to catch MissingArgumentsError, you're doing something really weird. I'm trying to think of a realistic example and failing.
This type of error is already caught and identified by Python as a TypeError in at least two different situations with the following (generic) error messages:
X takes Y positional arguments but Z was given
X missing 2 required positional arguments: Y and Z
The fact that two such messages exist is indicative that this type of error do occur in realistic code.
So, in theory, one could think of extracting these two subcases of TypeError into a new exception subclass; personnally, I think that the current error messages are sufficient and adding a new exception subclass for them would just make the exception handling code more complicated than necessary.
But the question is: why would you actually catch these different exception types, and what would you do when you caught them? There's actual code that checked for errno on an IOError, which is why it was easy to say that adding FileNotFoundError, etc. would be an improvement. Where's the code that catches (or wants to catch) MissingArgumentsError? I do think that every exception subclass that anyone wants to add is going to go through a case-by-case scrutiny. We're not just going to say "let's add a lot more exceptions!". Eric
André Roberge
> 2. When I get an error, especially from some shitty corporate system that > truncates the traceback, I want to get as many hints as possible about what > went wrong.
Then you should read the exception reason. You're still going to have to think about what your program logic error is, and a separate exception is going to be less use than an explanatory text string.
-- Rhodri James *-* Kynesim Ltd _______________________________________________ Python-ideas mailing list -- python-ideas@python.org <mailto:python-ideas@python.org> To unsubscribe send an email to python-ideas-leave@python.org <mailto:python-ideas-leave@python.org> https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/4ZKBKM... Code of Conduct: http://python.org/psf/codeofconduct/
_______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/OER543... Code of Conduct: http://python.org/psf/codeofconduct/
On Fri, 1 May 2020 10:21:22 -0300 André Roberge <andre.roberge@gmail.com> wrote:
On Fri, May 1, 2020 at 10:15 AM Rhodri James <rhodri@kynesim.co.uk> wrote:
On 01/05/2020 07:48, Ram Rachum wrote:
There are 2 reasons I want this:
1. When I'm writing a try..except clause, I want to catch a specific exception like MissingArgumentsError rather than ValueError or TypeError. They're too ubiquitous. I don't want some other unexpected failure producing the same ValueError and triggering my except clause.
If you want to catch MissingArgumentsError, you're doing something really weird. I'm trying to think of a realistic example and failing.
This type of error is already caught and identified by Python as a TypeError in at least two different situations with the following (generic) error messages:
X takes Y positional arguments but Z was given
X missing 2 required positional arguments: Y and Z
The fact that two such messages exist is indicative that this type of error do occur in realistic code.
"Realistic code"? Those are both effectively syntax (i.e., compile time) errors, and should be caught way before any code hits the street. I agree with Rhodri: if you're catching these errors at runtime, then you're doing something really weird. (And I agree with others: if you're catching these errors in a REPL or in unit tests, then you're going to have to think about how to fix the problem regardless of the label of the exception.) (Well, if I use my imagination: someone else is in control of your environment, and they pulled in a new version of some library that doesn't care about backwards compatility, and suddenly the runtime behavior of your otherwise tested and debugged code is now failing. But that hardly seems like a common scenario.) (Or maybe you're trying to build a genetic code generator and/or debugger, and it's easier on your software to "guide" the evolution if more information is encoded in the type of the exception rather than in a human readable message. But that also seems rather unlikely.) Dan -- “Atoms are not things.” – Werner Heisenberg Dan Sommers, http://www.tombstonezero.net/dan
On 2020-05-01 3:48 a.m., Ram Rachum wrote:
Hi,
Here's something I wanted in Python for many years. If this has been discussed in the past, please refer me to that discussion.
On one hand, it's something that I can't imagine the python-dev community supporting. On the other hand, it would maintain backward compatibility.
I wish there were a 100 more built-in exceptions in Python, that will be very specific about what went wrong.
If I do this:
>>> x, y = range(3)
I know it'll raise a ValueError, because I've memorized that, but it did take me a few years to remember where I should expect ValueError and where I should expect TypeError.
It would be nice if the operation above raised UnpackingOverflowError, which will be a subclass of UnpackingError, along with UnpackingUnderflowError. UnpackingError can be a subclass of ValueError, for backward compatibility.
oh yes please let me distinguish the exception from the unpacking and the exception from the iterator def foo(): yield from () raise ValueError it = foo() try: x, y = it except ValueError: print("Is this a problem with the unpacking or a problem with the generator?") raise also please have unpacking wrap any UnpackingError from the generator into a RuntimeError.
Similarly, if I did this:
>>> def f(x, y): return x + y >>> f(1)
I would get a TypeError. Would be a lot cooler if I got MissingArgumentsError, which would be a subclass of SignatureError, which would be a subclass of TypeError.
There are 2 reasons I want this:
1. When I'm writing a try..except clause, I want to catch a specific exception like MissingArgumentsError rather than ValueError or TypeError. They're too ubiquitous. I don't want some other unexpected failure producing the same ValueError and triggering my except clause.
2. When I get an error, especially from some shitty corporate system that truncates the traceback, I want to get as many hints as possible about what went wrong.
It's true that today, most Python exceptions have good text in their message, like "TypeError: f() missing 1 required positional argument: 'y'". But that isn't guaranteed everywhere, and specific exception types could help.
What do you think?
Ram.
_______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/XZXWXI... Code of Conduct: http://python.org/psf/codeofconduct/
Executive summary: 1. Don't distinguish exceptions if it won't make a difference to how you handle them (Andrew also makes this point, and I think it's implicit in MAL's post about exception vs. the reason as well). 2. Usefully introducing more fine-grained exceptions requires that *other* people use them when *you* want them to. Both introspection into my own behavior and the complaint about "shitty corporate systems" suggests that's a "Not Gonna Happen" for all of the proposed exceptions (I think this is Steven d'Aprano's point, too). Ram Rachum writes:
If I do this:
>>> x, y = range(3)
It would be nice if the operation above raised UnpackingOverflowError, which will be a subclass of UnpackingError, along with UnpackingUnderflowError. UnpackingError can be a subclass of ValueError, for backward compatibility.
It's not a matter of backward compatibility (unless the Exception class to raise was chosen incorrectly in the first place). The point of distinguishing among Exceptions is that you want to handle them differently. But there's not much you can do with a ValueError or TypeError, because Python implements the "termination model" of exception handling. That is, the program stops what it's doing, and throws away all the work done to that point, including the continuation. It's generally going to be somewhere between impractical and impossible to determine how to fix the input to the computation and restart it. You either need to fix the program and run it on the same inputs, or go on to the next input. Even if you raise a specific subclass, I'm probably going to handle it the same as any other ValueError. On the other hand, there are a plethora of environment errors, such as 3 suberrors of ConnectionError, at least 6 different errors related to opening filesystem objects, and so on. Why? Because there are well- known, common strategies allowing programs to recover from such errors, and programs that catch them are prepared for them with specific handlers for each such exception. I admit I don't recall offhand ever making a pragmatic distinction between TypeError and ValueError myself, except that ValueErrors are often validation errors (eg, a typo), while TypeErrors are invariably a logic error in my experience. But that's a difference in how I respond, not in how my program does. So the principle "don't make distinctions without a difference" isn't followed 100% (at least in terms of my personal pragmatics). But nothing's perfect. The best we can do is to not make things worse.
Similarly, if I did this:
>>> def f(x, y): return x + y >>> f(1)
I would get a TypeError. Would be a lot cooler if I got MissingArgumentsError, which would be a subclass of SignatureError, which would be a subclass of TypeError.
Cool, maybe, but the relevant question for distinguishing a new exception is how would your *program* respond differently to a different subclass of SignatureError or TypeError?
There are 2 reasons I want this:
1. When I'm writing a try..except clause, I want to catch a specific exception like MissingArgumentsError rather than ValueError or TypeError. They're too ubiquitous. I don't want some other unexpected failure producing the same ValueError and triggering my except clause.
If you're writing the code that raises the Exception, I see no problem except your willingness to define the appropriate subclasses of ValueError. :-) For SomebodyElse'sCode, here's the rub:
2. When I get an error, especially from some shitty corporate system that truncates the traceback, I want to get as many hints as possible about what went wrong.
Why do you think "shitty corporate systems" (or quick hacks on PyPI) are going to use fine-grained exceptions (a) correctly (b) at all? (This is a real question, despite my obvious priors as to the answer.)
It's true that today, most Python exceptions have good text in their message, like "TypeError: f() missing 1 required positional argument: 'y'". But that isn't guaranteed everywhere, and specific exception types could help.
I don't see much chance of that pragmatically. I write a lot of code that just raises RuntimeError because I don't feel like looking up and figuring out the appropriate subclass. Some of that code makes it into production, to my later chagrin. (Mostly not: "replace all RuntimeErrors with something appropriate" is on my personal review checklist. :-) IMO, if developers aren't willing to write a descriptive message, the effort to choose a specific exception appropriately is unlikely to be worth it to them. That effort is at best strictly convex, and quite possibly exponential, in the number of Exceptions defined.
participants (16)
-
Alex Hall
-
Andrew Barnert
-
André Roberge
-
Christopher Barker
-
Dan Sommers
-
Eric V. Smith
-
Ethan Furman
-
Henk-Jaap Wagenaar
-
M.-A. Lemburg
-
Peter Otten
-
Ram Rachum
-
Rhodri James
-
Serhiy Storchaka
-
Soni L.
-
Stephen J. Turnbull
-
Steven D'Aprano