← index #6175Issue #624
Related · high · value 0.451
QUERY · ISSUE

Lookup of operator builtins can fail with multiple inheritance

openby nickovsopened 2020-06-19updated 2020-06-21
py-core

When looking up a method that implements an operator (such as __int__() or __lshift__()) on an object who's class uses multiple inheritance, and an earlier base classes is a native type that does not implement that operator, then an implementation of the required method will not be found in a later base class that does implement it.

Take for instance the following example:

class Foo:
    def __int__(self):
        return 123

class Bar(Foo, list):
    pass

class Baz(list, Foo):
   pass

print(int(Bar([])))
print(int(Baz([])))

On C Python this prints the expected:

123
123

On Micropython the second call raises a TypeError:

123
Traceback (most recent call last):
  File "<stdin>", line 12, in <module>
TypeError: can't convert list to int

This is because mp_obj_class_lookup() checks to see if a native type has the necessary unary_op or binary_op function pointer but does not check to see if the specific operator code is implemented. If a native type is found that has the required operator function pointer the search for an implementation stops there, even if it doesn't implement the desired operator code, preventing a later, usable implementation from being found.

CANDIDATE · ISSUE

To support multiple inheritance, should filter out "object" from base class spec and handle it as implicit fallback

closedby pfalconopened 2014-05-22updated 2022-06-23
bug

We're not going to get proper Python MRO any time soon (#525), but to at least not break multiple inheritance (MI) in obvious way, we should at least do following:

  1. Filter out "object" from any explicit base class specification. (turn class Foo(object) into class Foo).
  2. Look up attributes in "object" as a fallback.

This will emulate the fact that in Python3, "object" is always the last type in MRO.

Failing to do the above may mean, that with our naive depth-first MRO, "object" ultimate base of first superclass in MI case may seize method call destined to another superclass, if "object" has such method. With #520 & #606, object now implements __new__ and __init__, so any other multi-superclass will be denied construction and initialization.

Keyboard

j / / n
next pair
k / / p
previous pair
1 / / h
show query pane
2 / / l
show candidate pane
c
copy suggested comment
r
toggle reasoning
g i
go to index
?
show this help
esc
close overlays

press ? or esc to close

copied