(Multiple) inheritance in Python and implicit APIs
The ultimate cause of our mystery with Django on Ubuntu 24.04 is that versions of Python 3.12
before 3.12.5 have a bug where builtin types in sub-interpreters
get unexpected additional slot wrappers (also), and Ubuntu 24.04
has 3.12.3. Under normal circumstances, 'list
' itself doesn't
have a '__str__
' method but instead inherits it from 'object
',
so if you have a class that inherits from '(list,YourClass)' and
YourClass defines a __str__
, the YourClass.__str__ is what
gets used. In a sub-interpreter, there is a list.__str__ and
suddenly YourClass.__str__ isn't used any more.
(mod_wsgi triggers this issue because in a straightforward configuration, it runs everything in sub-interpreters.)
This was an interesting bug, and one of the things it made me realize
is that the absence of a __str__ method on 'list
' itself had
implicitly because part of list
's API. Django had set up class
definitions that were 'class Something(..., list, AMixin)', where
the 'AMixin' had a direct __str__ method, and Django expected
that to work. This only works as long as 'list
' doesn't have its
own __str__ method and instead gets it through inheritance from
object.__str__. Adding such a method to 'list
' would break
Django and anyone else counting on this behavior, making the lack
of the method an implicit API.
(You can get this behavior with more or less any method that people might want to override in such a mixin class, but Python's special methods are probably especially prone to it.)
Before I ran into this issue, I probably would have assumed that where in the class tree a special method like __str__ was implemented was simply an implementation detail, not something that was visible as part of a class's API. Obviously, I would have been wrong. In Python, you can tell the difference and quite easily write code that depends on it, code that was presumably natural to experienced Python programmers.
(Possibly the existence of this implicit API was obvious to experienced Python programmers, along with the implication that various builtin types that currently don't have their own __str__ can't be given one in the future.)
|
|