Thanks to visit codestin.com
Credit goes to github.com

Skip to content

RecursionError when substracting System Decimal from Python Decimal #2240

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
sensokame opened this issue Sep 19, 2023 · 3 comments · Fixed by #2327
Closed

RecursionError when substracting System Decimal from Python Decimal #2240

sensokame opened this issue Sep 19, 2023 · 3 comments · Fixed by #2327
Labels

Comments

@sensokame
Copy link

Environment

  • Pythonnet version: Version: 3.0.1
  • Python version: 3.10
  • Operating System: Any (Windows/Linux)
  • .NET Runtime: 6.0.0

Details

  • Trying to substract System Decimal from Python Decimal (the other way works fine)

    TODO

  • import both system decimal and python decimal

  • create a separate variable from each type

  • subtract System Decimal from Python Decimal

    from System import Decimal as SysDecimal
    from decimal import Decimal as PyDecimal
    sys_dec = SysDecimal(1)
    py_dec = PyDecimal(1)
    print(str(sys_dec - py_dec )) # works fine
    print(str(py_dec - sys_dec)) # crashes
  • error thrown
    Traceback (most recent call last):
    File "", line 1, in
    RecursionError: maximum recursion depth exceeded while calling a Python object
@dariooo512
Copy link

I have the same issue:

same setup, here is my case:

var offers = new [] { new {Discount = 2.0m}};
Runtime.PythonDLL = @"C:\Python311\python311.dll";
PythonEngine.Initialize();
PythonEngine.BeginAllowThreads();
using (Py.GIL())
{
    File.WriteAllText("script.py", @"
def execute(offers):
    total_discount = 0
    for offer in offers:
        total_discount += offer.Discount
    average_discount = total_discount / len(offers)
    return average_discount
");
    var script = Py.Import("script");
    var result = script.InvokeMethod("execute", offers.ToPython());
}

maximum recursion depth exceeded while calling a Python object
File [...], line 4, in execute
total_discount += offer.Discount

hope it helps

@filmor
Copy link
Member

filmor commented Sep 23, 2023

I'll have a look into the recursion (which I can reproduce and is a bug), but we don't actually by default provide conversions from/to Python decimals. You must be running a non-upstream version of Python.NET or have more code that you are not showing.

@filmor filmor added the bug label Oct 16, 2023
@gertdreyer
Copy link
Contributor

gertdreyer commented Feb 21, 2024

I have the same issue.

Pythonnet version: 3.0.3
Pythonnet version: 3.11
Dotnet 6.0: Windows and Linux

For a codec I am using a naive string based conversion of cs System.Decimal to py decimal.Decimal and the inverse, in our use case the possible loss of precision should not be a concern.

I have traced it down to the __rsub__ operation on the cs type.

Operation Status
cs - py Working
cs.__sub__(py) Working
py - cs RecursionError
py.__sub__(cs) NotImplemented
cs.__rsub__(py) RecursionError

The same happens with multiply and addition right-hand methods as well. As per the python rules for operators the forward operators are tried first and if they are NotImplemented for a type the right hand ones are tried where the RecursionError occurs. The codec conversion does not get called for the python decimal foward operators which results in the python decimal conversion code being called which throws the NotImplemented exception

Codec:

public class DecimalCodec : IPyObjectDecoder, IPyObjectEncoder
    {
        public bool CanDecode(PyType objectType, Type targetType)
        {
            return objectType.Name == "decimal.Decimal" && targetType == typeof(decimal);
        }

        public bool CanEncode(Type type)
        {
            return typeof(decimal) == type;
        }

        public bool TryDecode<T>(PyObject pyObj, out T? value)
        {
            if (typeof(T) != typeof(decimal))
            {
                value = default(T);
                return false;
            }
            try
            {
                string decstr = (pyObj as dynamic).__str__();
                bool parseSuccess = decimal.TryParse(decstr, out decimal csdeci);
                value = (T)Convert.ChangeType(csdeci, typeof(decimal));
                return parseSuccess;
            }
            catch (Exception ex)
            {
                value = default(T);
                return false;
            }
        }

        public PyObject? TryEncode(object value)
        {
            if (typeof(decimal) != value.GetType())
            {
                return null;
            };
            try
            {
                dynamic pydeci = PyModule.Import("decimal");
                dynamic thedecimal = pydeci.Decimal(value.ToString());
                return thedecimal;
            }
            catch (Exception ex)
            {
                return null;
            }
        }
    }

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants