Support for __rlt__, __rle__, ...

C programming, build, interpreter/VM.
Target audience: MicroPython Developers.
Post Reply
mpy-dev
Posts: 8
Joined: Tue Aug 03, 2021 3:00 pm

Support for __rlt__, __rle__, ...

Post by mpy-dev » Tue Aug 03, 2021 3:12 pm

Does micropython support __rlt__, __rle__, __req__, __rne__, __rgt__ and __rge__?
I include them in my class to compare 'int' numbers to class objects but micropython does not seem to call them. An exception of this kind occurs: "TypeError: unsupported types for __lt__"
The same code works with Python.

User avatar
jimmo
Posts: 2754
Joined: Tue Aug 08, 2017 1:57 am
Location: Sydney, Australia
Contact:

Re: Support for __rlt__, __rle__, ...

Post by jimmo » Wed Aug 04, 2021 5:29 am

mpy-dev wrote:
Tue Aug 03, 2021 3:12 pm
Does micropython support __rlt__, __rle__, __req__, __rne__, __rgt__ and __rge__?
Yes, but only when MICROPY_PY_REVERSE_SPECIAL_METHODS is enabled at compile time. For some reason it isn't enabled on ESP32, but is on STM32 and Pico.

User avatar
jimmo
Posts: 2754
Joined: Tue Aug 08, 2017 1:57 am
Location: Sydney, Australia
Contact:

Re: Support for __rlt__, __rle__, ...

Post by jimmo » Wed Aug 04, 2021 5:38 am


mpy-dev
Posts: 8
Joined: Tue Aug 03, 2021 3:00 pm

Re: Support for __rlt__, __rle__, ...

Post by mpy-dev » Wed Aug 04, 2021 6:13 am

jimmo wrote:
Wed Aug 04, 2021 5:29 am
mpy-dev wrote:
Tue Aug 03, 2021 3:12 pm
Does micropython support __rlt__, __rle__, __req__, __rne__, __rgt__ and __rge__?
Yes, but only when MICROPY_PY_REVERSE_SPECIAL_METHODS is enabled at compile time. For some reason it isn't enabled on ESP32, but is on STM32 and Pico.
That's strange. I am using a Raspberry Pi Pico:
Implementation name: micropython
Implementation version: 1.16.0
Implementation platform: rp2

User avatar
jimmo
Posts: 2754
Joined: Tue Aug 08, 2017 1:57 am
Location: Sydney, Australia
Contact:

Re: Support for __rlt__, __rle__, ...

Post by jimmo » Wed Aug 04, 2021 12:07 pm

mpy-dev wrote:
Wed Aug 04, 2021 6:13 am
That's strange. I am using a Raspberry Pi Pico:
huh... maybe could you share your code? Or a simple snippet that shows the problem?

mpy-dev
Posts: 8
Joined: Tue Aug 03, 2021 3:00 pm

Re: Support for __rlt__, __rle__, ...

Post by mpy-dev » Wed Aug 04, 2021 3:10 pm

jimmo wrote:
Wed Aug 04, 2021 12:07 pm
mpy-dev wrote:
Wed Aug 04, 2021 6:13 am
That's strange. I am using a Raspberry Pi Pico:
huh... maybe could you share your code? Or a simple snippet that shows the problem?
After investigating a bit further, I can see that the problem is related to reverse operators, but not as I thought. This is a small bit of code that shows the problem:

Code: Select all

import sys

if sys.implementation.name == "micropython":
    import machine

class TestNumber():

    def __init__(self, n: int) -> None:
        self.number = n

    def __lt__(self, other: "TestNumber") -> bool:
        print(self, "__lt__", other)
        if isinstance(other, int):
            other = TestNumber(other)
        return (self.number < other.number)

    def __gt__(self, other: "TestNumber") -> bool:
        print(self, "__gt__", other)
        if isinstance(other, int):
            other = TestNumber(other)
        return (self.number > other.number)

    def __floordiv__(self, other: "TestNumber") -> "TestNumber":
        print(self, "__floordiv__", other)
        if isinstance(other, int):
            other = TestNumber(other)
        return TestNumber(self.number // other.number)

    def __rfloordiv__(self, other: int) -> "DecimalNumber":
        print(self, "__rfloordiv__", other)
        return TestNumber(other).__floordiv__(self)

    def __str__(self) -> str:
        return str(self.number)

print("Implementation name:", sys.implementation.name)
print("Implementation version:", "{0}.{1}.{2}".format(
    sys.implementation.version[0],
    sys.implementation.version[1],
    sys.implementation.version[2]
    )
)
print("Implementation platform:", sys.platform)

b = TestNumber(5)

print("-" * 78)
print("b =", b)

print("-" * 78)
try:
    if (3).__lt__(b) is NotImplemented:
        print("'int' < 'TestNumber' not implemented. Python should try b.__gt__(3)")
    print("3 < b: ", (3 < b))
except Exception as e:
    print("Exception: ", e)

print("-" * 78)
try:
    if (15).__floordiv__(b) is NotImplemented:
        print("'int' // 'TestNumber' not implemented. Python should try b.__rfloordiv__(15)")
    print("15 // b =", 15 // b)
except Exception as e:
    print("Exception: ", e)
Executed on a PC, the result is:

Code: Select all

Implementation name: cpython
Implementation version: 3.6.9
Implementation platform: linux
------------------------------------------------------------------------------
b = 5
------------------------------------------------------------------------------
'int' < 'TestNumber' not implemented. Python should try b.__gt__(3)
5 __gt__ 3
3 < b:  True
------------------------------------------------------------------------------
'int' // 'TestNumber' not implemented. Python should try b.__rfloordiv__(15)
5 __rfloordiv__ 15
15 __floordiv__ 5
15 // b = 3
Executed on a Raspberry Pi Pico, the result is:

Code: Select all

Implementation name: micropython
Implementation version: 1.16.0
Implementation platform: rp2
------------------------------------------------------------------------------
b = 5
------------------------------------------------------------------------------
Exception: 'int' object has no attribute '__lt__'
------------------------------------------------------------------------------
Exception: 'int' object has no attribute '__floordiv__'
I have revised the section MicroPython differences from CPython and I could not find this case.

User avatar
pythoncoder
Posts: 5956
Joined: Fri Jul 18, 2014 8:01 am
Location: UK
Contact:

Re: Support for __rlt__, __rle__, ...

Post by pythoncoder » Thu Aug 05, 2021 4:49 am

I see the same result on the Unix build and on a Pyboard. I'm no expert on the build system but all seem to be defined with MICROPY_PY_REVERSE_SPECIAL_METHODS set.

Code: Select all

[adminpete@capybara]: /mnt/qnap2/data/Projects/MicroPython/micropython/ports
$ grep --include=\*.h -rnw '.' -e "MICROPY_PY_REVERSE_SPECIAL_METHODS"
./rp2/mpconfigport.h:87:#define MICROPY_PY_REVERSE_SPECIAL_METHODS      (1)
./mimxrt/mpconfigport.h:82:#define MICROPY_PY_REVERSE_SPECIAL_METHODS  (1)
./qemu-arm/mpconfigport.h:29:#define MICROPY_PY_REVERSE_SPECIAL_METHODS (1)
./stm32/mpconfigport.h:104:#define MICROPY_PY_REVERSE_SPECIAL_METHODS (1)
./windows/mpconfigport.h:82:#define MICROPY_PY_REVERSE_SPECIAL_METHODS (1)
./unix/variants/coverage/mpconfigvariant.h:44:#define MICROPY_PY_REVERSE_SPECIAL_METHODS (1)
./unix/mpconfigport.h:104:#define MICROPY_PY_REVERSE_SPECIAL_METHODS (1)
FWIW my quaternion class uses a lot of these special methods and has run on a Pyboard. However it doesn't use __lt__ or __floordiv__ so maybe these are special cases of missing methods?
Peter Hinch
Index to my micropython libraries.

User avatar
jimmo
Posts: 2754
Joined: Tue Aug 08, 2017 1:57 am
Location: Sydney, Australia
Contact:

Re: Support for __rlt__, __rle__, ...

Post by jimmo » Thu Aug 05, 2021 5:15 am

mpy-dev wrote:
Wed Aug 04, 2021 3:10 pm
After investigating a bit further, I can see that the problem is related to reverse operators, but not as I thought.
I see my misunderstanding now -- the topic of the post was "Support for __rlt__, __rle__, ..." which is what made me think reverse operators, but looking more closely, there is no such thing as reverse (i.e. __r...__) comparison operators and the behavior you're expecting is a different feature.

Specifically, at https://docs.python.org/3/reference/dat ... bject.__ge__

"There are no swapped-argument versions of these methods (to be used when the left argument does not support the operation but the right argument does); rather, __lt__() and __gt__() are each other’s reflection, __le__() and __ge__() are each other’s reflection, and __eq__() and __ne__() are their own reflection. If the operands are of different types, and right operand’s type is a direct or indirect subclass of the left operand’s type, the reflected method of the right operand has priority, otherwise the left operand’s method has priority. Virtual subclassing is not considered."

MicroPython does not currently implement this behaviour... although it probably should.

The other thing we don't do is support the operators returning NotImplemented... which is why in your sample program you get the error about "Exception: 'int' object has no attribute '__lt__'" which comes from the

Code: Select all

if (3).__lt__(b) is NotImplemented:
line, and not the actual use of the < operator.

mpy-dev
Posts: 8
Joined: Tue Aug 03, 2021 3:00 pm

Re: Support for __rlt__, __rle__, ...

Post by mpy-dev » Thu Aug 05, 2021 1:43 pm

jimmo wrote:
Thu Aug 05, 2021 5:15 am

. . . . .

MicroPython does not currently implement this behaviour... although it probably should.

The other thing we don't do is support the operators returning NotImplemented... which is why in your sample program you get the error about "Exception: 'int' object has no attribute '__lt__'" which comes from the

Code: Select all

if (3).__lt__(b) is NotImplemented:
line, and not the actual use of the < operator.
Thanks a lot for your help, jimmo.

I also think that it is a core feature that should probably be implemented, but I do not know what it will require or the difficulty/possibility of adding it to micropython.
I find it limiting. I have finished the development of a module for micropython in the line of 'decimal', part of the Python Standard Library, to provide floating point arithmetic with arbitrary precision to micropython. Without the functionality we have been discussing, it can work on micropython as long as operations are performed between class objects or, when using 'int' and class objects, 'int' is used on the right hand side of the operator. For example, (a > 5) is possible but (5 < a) is not.

By the way, it will be available on GitHub as soon as I finish the testing and documentation.

Post Reply