modularml / mojo

The Mojo Programming Language
https://docs.modular.com/mojo/manual/
Other
23.38k stars 2.6k forks source link

[BUG] NotImplemented when trying to do true division #225

Open wizlee opened 1 year ago

wizlee commented 1 year ago

Bug Description

Got NotImplemented as the error message instead of 'Int' does not implement the '__truediv__' method. The later make me realized what is not implemented and enables me to find this relevant feature request -> #53

Unexpected Behaviour

The following code outputs NotImplemented.

let psutil = Python.import_module("psutil")
print(psutil.virtual_memory().total/ (1024.0 **3))

Expected Behaviour

Instead I am expecting it to returns 'Int' does not implement the '__truediv__' method similar to the output of the following code.

print(9/(1 **3))

Steps to Reproduce

  1. Run the code snippet shown in the unexpected behaviour section above.
  2. Screenshot of the output to provide more context in case I mis-understood the code.

image

Context

11:devices:/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-podb759e618_d78b_4774_b5a1_32694b325e5b.slice/cri-containerd-3725388e157d8fb3b248b325bf80b7f27f927fa1ad9b7bb50846e0ea1b50d369.scope
10:memory:/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-podb759e618_d78b_4774_b5a1_32694b325e5b.slice/cri-containerd-3725388e157d8fb3b248b325bf80b7f27f927fa1ad9b7bb50846e0ea1b50d369.scope
9:cpuset:/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-podb759e618_d78b_4774_b5a1_32694b325e5b.slice/cri-containerd-3725388e157d8fb3b248b325bf80b7f27f927fa1ad9b7bb50846e0ea1b50d369.scope
8:freezer:/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-podb759e618_d78b_4774_b5a1_32694b325e5b.slice/cri-containerd-3725388e157d8fb3b248b325bf80b7f27f927fa1ad9b7bb50846e0ea1b50d369.scope
7:net_cls,net_prio:/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-podb759e618_d78b_4774_b5a1_32694b325e5b.slice/cri-containerd-3725388e157d8fb3b248b325bf80b7f27f927fa1ad9b7bb50846e0ea1b50d369.scope
6:cpu,cpuacct:/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-podb759e618_d78b_4774_b5a1_32694b325e5b.slice/cri-containerd-3725388e157d8fb3b248b325bf80b7f27f927fa1ad9b7bb50846e0ea1b50d369.scope
5:pids:/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-podb759e618_d78b_4774_b5a1_32694b325e5b.slice/cri-containerd-3725388e157d8fb3b248b325bf80b7f27f927fa1ad9b7bb50846e0ea1b50d369.scope
4:blkio:/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-podb759e618_d78b_4774_b5a1_32694b325e5b.slice/cri-containerd-3725388e157d8fb3b248b325bf80b7f27f927fa1ad9b7bb50846e0ea1b50d369.scope
3:hugetlb:/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-podb759e618_d78b_4774_b5a1_32694b325e5b.slice/cri-containerd-3725388e157d8fb3b248b325bf80b7f27f927fa1ad9b7bb50846e0ea1b50d369.scope
2:perf_event:/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-podb759e618_d78b_4774_b5a1_32694b325e5b.slice/cri-containerd-3725388e157d8fb3b248b325bf80b7f27f927fa1ad9b7bb50846e0ea1b50d369.scope
1:name=systemd:/kubepods.slice/kubepods-burstable.slice/kubepods-burstable-podb759e618_d78b_4774_b5a1_32694b325e5b.slice/cri-containerd-3725388e157d8fb3b248b325bf80b7f27f927fa1ad9b7bb50846e0ea1b50d369.scope
0::/
DayDun commented 1 year ago

The problem is not that Int doesn't implement division. The problem is that Python's own int.__truediv__ does not implement division with floats.

Same thing happens in regular Python:

>>> (5).__truediv__(2.0)
NotImplemented

I'm out of my depth when it comes to details like this, but it appears like BINARY_OP (/) in Python is in fact not identical to just calling __truediv__.

Workaround:

from CPython import PyObjectPtr
from PythonInterface import Python, _get_global_python_itf

let builtins = Python.import_module("builtins")

print(PythonObject(5) / 2.0)

fn truediv(a: PythonObject, b: PythonObject) -> PythonObject:
    var cpython = _get_global_python_itf().cpython
    let out = cpython.lib.get_function[
        fn (PyObjectPtr, PyObjectPtr) -> PyObjectPtr
    ]("PyNumber_TrueDivide")(a.pyObject, b.pyObject)
    cpython._inc_total_rc()
    return out

print(truediv(PythonObject(5), 2.0))
Mogball commented 1 year ago

@DayDun is correct. The Python interpreter implements special logic for mixed arithmetic between int and float. The NotImplemented error is coming from CPython. PythonObject will just need to implement the special logic itself FYI @stumpOS

DayDun commented 1 year ago

Why not just call PyNumber_TrueDivide? I assumed that does all the special logic needed. Or is the plan to gradually implement more and more CPython logic in mojo itself?

Mogball commented 1 year ago

Right. PythonObject will need to specially check for mixed arithmetic and do the right thing, whatever that may be.

Mogball commented 1 year ago

@stumpOS can you take a look at this?

Brian-M-J commented 5 months ago

What is the progress on this? I just ran this code on the 5th July 2024 version of the Mojo Playground:

from python import Python

fn main() raises:
    print(5 // 2.0)
    print(Python.evaluate("5") // 2.0)
    print(5 // Python.evaluate("2.0"))

and got this output:

2.0
NotImplemented
-5
TypeError: 'float' object cannot be interpreted as an integer

Running this:

from python import Python

fn main() raises:
    print(5 // 2.0)
    print(Python.evaluate("5") // 2.0)
    print(5 // Python.evaluate("2.0"))
    print(Python.evaluate("5") // Python.evaluate("2.0"))  # 1 line added here

gives this:

Run error:

2.0
NotImplemented
-5

(The text is in red, so my guess is it's printed to stderr?)

For reference, running 5 // 2.0 in Python gives 2.0.