py2many / py2many

Transpiler of Python to many other languages
MIT License
610 stars 46 forks source link

Generated exception_names.jl doesnt work #570

Open jayvdb opened 5 months ago

jayvdb commented 5 months ago

Python

    try:
        3 / 0
    except ZeroDivisionError:
        print("ZeroDivisionError")

successfully transpiles to

    try
        3 / 0
    catch exn
        if exn isa ZeroDivisionError
            println(join(["ZeroDivisionError"], " "))
        end
    end

But that doesnt detect the divide by zero.

This is a fairly high priority as it is the only case where pyjl transpiles successfully but the generated code isnt correct.

MiguelMarcelino commented 5 months ago

I believe this is one scenario that we will have to discuss. The Python ZeroDivisionError exception is not considered an exception in Julia. Julia will instead return Inf, which represents infinity. I don't know how we will be able to convert this code to Julia, since we will essentially be changing its semantics.

jayvdb commented 5 months ago

The simplest approach right now is to create a failure in the transpiler to indicate that the input code isnt able to be transpiled. i.e. in visit_ExceptHandler, raise an AstNotImplementedError if type_str is ZeroDivisionError, or any other python exception which is not able to be transpiled to Julia.

The harder approach is to create a rewriter. The way I would do this is to detect except ZeroDivisionError, and then wrap every single expr in the try block with a custom exception throw. e.g. the revised Python becomes

    def raise_ZeroDivisionError(val):
        if isnan(val) or isinf(val):
            raise ZeroDivisionError
        return val

    try:
        raise_ZeroDivisionError(3 / 0)
    except ZeroDivisionError:
        print("ZeroDivisionError")

exception_names.py should also be enhanced to ensure that the rewriter/transpiler is injecting raise_ZeroDivisionError for any non-constant assignment. i.e.

    try:
        n = 3
        d = 0
        val = n / d
        print(val)
    except ZeroDivisionError:
        print("ZeroDivisionError")

should be rewritten to

    try:
        n = 3
        d = 0
        val = raise_ZeroDivisionError(n / d)
        print(val)
    except ZeroDivisionError:
        print("ZeroDivisionError")
MiguelMarcelino commented 5 months ago

It sounds like a possible approach. I'll give it some thought and may come up with a rewriter for it.

jayvdb commented 5 months ago

If you do get a rewriter working, IMO put it into py2many/rewriters.py, as it is very likely that a rewriter like that would be able to make that test work on many languages. Only the isnan(val) or isinf(val) would need to be altered for each language.