Skip to content

What would it take to implement built-in conversion to and from System.Decimal? #2319

Description

@siegfriedpammer

Environment

  • Pythonnet version: 3.0.3
  • Python version: 3.8.1+
  • Operating System: Windows 10
  • .NET Runtime: .NET 6

Details

  • Describe what you were trying to get done.

    I implemented a pythonnet codec for System.Decimal:

internal class DecimalCodec : IPyObjectDecoder, IPyObjectEncoder
{
    public bool CanDecode(PyType objectType, Type targetType)
    {
        return targetType == typeof(decimal);
    }

    public bool CanEncode(Type type)
    {
        return type == typeof(decimal)
            || type == typeof(object); // this is necessary to get support for boxed decimals
    }

    public bool TryDecode<T>(PyObject pyObj, out T? value)
    {
        if (decimal.TryParse(pyObj.ToString(CultureInfo.InvariantCulture), out decimal val))
        {
            value = (T)(object)val;
            return true;
        }
        else
        {
            value = default;
            return false;
        }
    }

    public PyObject? TryEncode(object value)
    {
        if (value is not decimal d)
        {
            return null;
        }
        return new PyFloat(d.ToString(CultureInfo.InvariantCulture));
    }
}

Basically I am trying to do things like the following:

num: int or float
is_equal_to_num = someObject.DecimalFieldOrProperty == num # works with my codec
s_equal_to_num = someObject.BoxedDecimalFieldOrProperty == num # works with my codec
SomeObject.SomeMethodAcceptingDecimal(num) # does not work, because Python class 'int' is not convertible to System.Decimal

>>> SomeObject.SomeMethodAcceptingDecimal(num)
Python.Runtime.PythonException: 'int' value cannot be converted to System.Decimal

The above exception was the direct cause of the following exception:

System.ArgumentException: 'int' value cannot be converted to System.Decimal in method Void SomeMethodAcceptingDecimal(System.Decimal)
 ---> Python.Runtime.PythonException: 'int' value cannot be converted to System.Decimal
   --- End of inner exception stack trace ---

The above exception was the direct cause of the following exception:

System.AggregateException: One or more errors occurred. ('int' value cannot be converted to System.Decimal in method Void SomeMethodAcceptingDecimal(System.Decimal))
 ---> System.ArgumentException: 'int' value cannot be converted to System.Decimal in method Void SomeMethodAcceptingDecimal(System.Decimal)
 ---> Python.Runtime.PythonException: 'int' value cannot be converted to System.Decimal
   --- End of inner exception stack trace ---
   --- End of inner exception stack trace ---

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: No method matches given arguments for SomeObject.SomeMethodAcceptingDecimal: (<class 'int'>)

I see a few issues with this approach:

  • It's not working (well) with overload resolution
  • It relies on strings to do the conversion, which is expensive. There must be a way to do it faster by accessing raw bits and python internals.
  • Support for boxed decimals is hacky, because the Codec gets called for every object. This could be solved by adding a object value parameter to CanEncode so it has access to the same information as Python.Runtime.Converter.ToPython, which uses the actual TypeCode of the value to handle boxed primitive types.

Note: I am willing to contribute and work with the pythonnet team to get this feature added to the pythonnet runtime.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions