Nathaniel Manista <nathan...@google.com> added the comment:

I’d like to try to steer this conversation back toward what I think is the 
actionable question: “does the exemplification of this practice in the Errors 
and Exceptions portion of The Python Tutorial bring about a net benefit or a 
net cost to its intended audience?”.

What is the benefit? It is that when an author happens to be authoring a module 
*all user-defined Exception subclasses of which satisfy some 
more-specific-than-Exception type* the author is led to define an intermediate 
class between Exception and the directly-used user-defined Exception subclasses 
capturing that type in a usable code element.

What readers of the tutorial enjoy this benefit? Pretty much only those authors 
(who happen to be authoring a module *all user-defined Exception subclasses of 
which satisfy some more-specific-than-Exception type*) who are still learning 
about classes and inheritance. That’s a doubly slim population, isn’t it? Maybe 
also those who kind of know, but aren’t so sure and could use some 
reinforcement from seeing in the tutorial something that they independently did 
on their own in their own code. I wouldn’t think that authors who already know 
with confidence and from experience about classes and inheritance would benefit 
from the example in the tutorial, so “In my experience as a teacher, the 
possibility doesn't usually occur to people without it having been suggested” 
comes as a surprise to me. But… okay, them too - but again, only when they 
happen to be authoring a module *all user-defined Exception subclasses of which 
satisfy some more-specific-than-Exception type*.

What is the cost? It is that when an author happens to be authoring a module 
that does *not* have the property that all user-defined Exception subclasses 
satisfy some more-specific-than-Exception type, the common intermediate class 
is just bloat. It’s noise disrupting the signal. It undermines the API design 
advice “when in doubt, leave it out”. It unnecessarily widens the module’s API. 
It undermines the API design advice “classes are the most complex kind of code 
element in common use so they have the highest value proposition to satisfy to 
justify their existence”. It violates the Liskov Substitution Principle. Maybe 
most importantly, it obscures other relationships among the user-defined 
Exception subclasses in the module such as a superclass shared by 
some-but-not-all of the module’s user-defined Exception subclasses, if such 
other relationships exist.

What readers of the tutorial pay this cost? Those who are still learning the 
language. And those who are following pattern and convention - note that the 
tutorial contains only one example of user-defined Exception subclasses, and… 
an unfortunate fact of life of tutorials is that readers are invited to 
generalize from single examples. And, as I think we’ve seen in this 
conversation, those who just picked up the practice at one point and have kept 
it going.

The Exception subclass hierarchy of sqlite3 that was mentioned earlier in this 
conversation demonstrates exactly this bloat and misapplication - there’s 
Warning, which is a direct subclass of Exception, and there’s Error, which is 
also a direct subclass of Exception and has the erroneous specification “The 
base class of the other exceptions in this module”, and there’s DatabaseError, 
which is a subclass of Error, and then there are IntegrityError, 
ProgrammingError, OperationalError, and NotSupportedError, which inherit from 
DatabaseError. What’s the use of Error? There are no code elements in the 
module specified as raising Error. There’s example code in the module showing 
“except sqlite3.Error”, but why shouldn’t that be “except 
sqlite3.DatabaseError”?

It’s a red herring is that the practice appears widely applied in existing 
Python code - well of course; it’s been exemplified in the tutorial for 
seventeen years! :-P

One last thing to consider: look at the example specifically - InputError and 
TransitionError. There’s no elsewhere-in-the-module example code showing a 
function that has “Error” in its “Raises:” specification and could raise either 
an InputError or a TransitionError, and there’s no outside-of-the-module 
example code showing a user of the module calling a module function and saving 
duplicated lines of code because they want to respond to InputErrors and 
TransitionErrors in exactly the same way.

We should remove the “Base class for exceptions in this module” Error class 
from the tutorial’s example because it just isn’t beneficial enough, in enough 
applicable modules, to enough authors, and it’s more than costly enough, in 
enough modules to which it doesn’t apply, and to enough authors, even just as 
noise and API bloat. I don’t know that this could have been calculated from 
first principles seventeen years ago; I think perhaps it took the experience of 
having the guidance out there, so rarely merited and so widely implemented, to 
see it being a net drag.

----------

_______________________________________
Python tracker <rep...@bugs.python.org>
<https://bugs.python.org/issue34538>
_______________________________________
_______________________________________________
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com

Reply via email to