
On Wed, Apr 20, 2022 at 03:43:44PM -0000, malmiteria wrote:
to give you exemples of problems : 1) let's start with a django problem : ``` class MyView(ModelView, PermissionMixin): pass ``` doesn't apply any of the PermissionMixin logic to the view. It doesn't raise a single error either.
Is it supposed to work, or is the bug in your code, not the framework? If it is a bug in the framework, have you reported it as a bug to Django, and if not, why not?
Since it's mostly made out of django stuff, it's likely there wouldn't be automated testing to check if the permissions are indeed required when attempting to visit whatever resource MyView serves. After all, automated testing should test your code, not the library you're importing's code.
You should be testing MyView to ensure that the permissions are required. If you don't have a test that MyView requires testing, then somebody could refactor MyView and remove the permission stuff, and you will never know.
The only way to tell those permission aren't applied, in this case, is to actually see the bug happen IRL.
Not the only way. The right way is to test MyView to ensure it is doing what you expect.
2) Another one, straight from django docs : https://docs.djangoproject.com/fr/4.0/topics/auth/default/#django.contrib.au...
Here's the link for those who can't read French: https://docs.djangoproject.com/en/4.0/topics/auth/default/#django.contrib.au... (Your English is much better than my French.) The problem here is explained in the docs: "Due to the way UserPassesTestMixin is implemented, you cannot stack them in your inheritance list." And then: "If TestMixin1 would call super() and take that result into account, TestMixin1 wouldn’t work standalone anymore." So the problem is that UserPassesTestMixin is not designed to be the top node in a diamond (that is, you can't inherit from it more than once). This is an example why multiple inheritance must be *cooperative*. Due to the tight coupling between classes in MI, you have to carefully design your classes to cooperate. I *guess* that the fundamental problem here is that Django is where Zope and Plone were six years ago: stuck with an overly complicated and complex framework based on MI, before their move to preferring composition(?) in version 4. (I'm *totally* guessing here: I'm not an expert on Django, or Zope. I could be wrong.)
Some would argue the proper way to do multiple inheritance implies having one single parent class all classes you inherit from do themselves inherit from.
This allows you to use super in those class, and still allows you to inherit from each of them individually, as needed, without risking a super call to target object, in which case you'd face the problem that object doesn't have whatever method you're trying to access.
Correct.
What happens when the common parent class raises NotImplemented errors?
The same as any other exception: if your class raises an exception, it is either an internal bug in the class, or a documented exception as part of the class' API. So if your common parent class raises NotImplementedError, that is either a bug in your common parent class, or a bug in your subclass, for triggering the condition that is documented as raising NotImplementedError.
You can't use super in any of it's child class, and any of its child class using super would work only under MI, if not the last parent inherited from.
The whole point of that common parent is to catch any methods before they hit the ultimate base class, object.
In other term, you render those 'independant' objects not independant anymore.
Classes in inheritance hierarchies are not independent. They are heavily dependent and tightly coupled. This is why people prefer the less-tightly coupled composition pattern over inheritance.
Note that this django docs explicitely states it is not possible to practice multiple inheritance in this case, which is my point :
Right. Because you have to design your classes very carefully to work correctly under MI, and Django have not done that in this case. (They may or may not have a good reason for that.)
people don't know a way out of super for MI cases, when super doesn't work.
3) My gobelin exemple is another case. What if you want to inherit from multiple parent 'as is', instead of having them ignore their respective parent (GP) because this parent (GP) is reoccuring in the inheritance tree?
We've answered this many times.
4) Lib refactoring are breaking changes
A Lib author refactoring his code by extracting a class as a parent class of multiple of the class provided is introducing a breaking change. Because any user's code inheriting from at least 2 of the class impacted by this refactoring will now exhibit a different behavior.
Right. MI is fragile like that, because it involves very tight coupling between classes.
If O1 and O2 are refactored into N1(GP) and N2(GP) the MRO as it was before refactoring was essentially N1, GP, N2, GP,
No, that's impossible. That is an inconsistent MRO: it has GP listed twice, which is a bug. That would mean that 1. methods from N2 would never be called; 2. methods from GP would be called twice; or 3. N2 cannot overload methods from GP, only replace them. All three alternatives are unacceptable in any consistent model for inheritance.
If your point is :
I'm afraid that person is going to have to put out the effort to learn what the MRO is and how it works
Then how are any lib user's supposed to account for this case 4? The only way is to never use MI with any class from any lib you're inheriting from...
Many languages have taken the approach to not support MI, because it is so hard to use correctly. E.g. Java.
And no, class.method doesn't work in this case either.
Right, because your refactoring broke the class' public interface. It seems to me that you have correctly identified problems with OOP and inheritance, discovered that it is not a panacea that solves all problems, but has problems of its own, but reacted to that by assuming that the problem must be super() and therefore all you need do is "fix" super(), when that's not the problem at all. -- Steve