Page 1 of 1

Support for __rlt__, __rle__, ...

Posted: Tue Aug 03, 2021 3:12 pm
by mpy-dev
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.

Re: Support for __rlt__, __rle__, ...

Posted: Wed Aug 04, 2021 5:29 am
by jimmo
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.

Re: Support for __rlt__, __rle__, ...

Posted: Wed Aug 04, 2021 5:38 am
by jimmo

Re: Support for __rlt__, __rle__, ...

Posted: Wed Aug 04, 2021 6:13 am
by mpy-dev
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

Re: Support for __rlt__, __rle__, ...

Posted: Wed Aug 04, 2021 12:07 pm
by jimmo
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?

Re: Support for __rlt__, __rle__, ...

Posted: Wed Aug 04, 2021 3:10 pm
by mpy-dev
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.

Re: Support for __rlt__, __rle__, ...

Posted: Thu Aug 05, 2021 4:49 am
by pythoncoder
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?

Re: Support for __rlt__, __rle__, ...

Posted: Thu Aug 05, 2021 5:15 am
by jimmo
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.

Re: Support for __rlt__, __rle__, ...

Posted: Thu Aug 05, 2021 1:43 pm
by mpy-dev
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.